<commit_before>// Copyright (c) 2020 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ProducerSideServiceImpl.h"

#include <thread>

#include "OrbitBase/Logging.h"

namespace orbit_service {

void ProducerSideServiceImpl::OnCaptureStartRequested(CaptureEventBuffer* capture_event_buffer) {
  CHECK(capture_event_buffer != nullptr);
  LOG("About to send StartCaptureCommand to CaptureEventProducers (if any)");
  {
    absl::WriterMutexLock lock{&capture_event_buffer_mutex_};
    capture_event_buffer_ = capture_event_buffer;
  }
  {
    absl::MutexLock lock{&service_state_mutex_};
    service_state_.capture_status = CaptureStatus::kCaptureStarted;
  }
}

void ProducerSideServiceImpl::OnCaptureStopRequested() {
  LOG("About to send StopCaptureCommand to CaptureEventProducers (if any)");
  {
    absl::MutexLock lock{&service_state_mutex_};
    service_state_.capture_status = CaptureStatus::kCaptureStopping;

    // Wait (for a limited amount of time) for all producers to send AllEventsSent or to disconnect.
    service_state_mutex_.AwaitWithTimeout(
        absl::Condition(
            +[](ServiceState* service_state) {
              return service_state->producers_remaining == 0 || service_state->exit_requested;
            },
            &service_state_),
        absl::Milliseconds(static_cast<int64_t>(max_wait_for_all_events_sent_ms_)));
    CHECK(service_state_.producers_remaining >= 0);
    if (service_state_.producers_remaining == 0) {
      LOG("All CaptureEventProducers have finished sending their CaptureEvents");
    } else {
      ERROR(
          "Stopped receiving CaptureEvents from CaptureEventProducers "
          "even if not all have sent all their CaptureEvents");
    }
    LOG("About to send CaptureFinishedCommand to CaptureEventProducers (if any)");
    service_state_.capture_status = CaptureStatus::kCaptureFinished;
    service_state_.producers_remaining = 0;
  }

  {
    absl::WriterMutexLock lock{&capture_event_buffer_mutex_};
    capture_event_buffer_ = nullptr;
  }
}

void ProducerSideServiceImpl::OnExitRequest() {
  {
    absl::MutexLock lock{&service_state_mutex_};
    service_state_.exit_requested = true;
  }

  LOG("Attempting to disconnect from CaptureEventProducers as exit was requested");
  {
    absl::MutexLock lock{&server_contexts_mutex_};
    for (grpc::ServerContext* context : server_contexts_) {
      // This should cause blocking Reads on ServerReaderWriter to fail immediately.
      context->TryCancel();
    }
  }

  {
    absl::WriterMutexLock lock{&capture_event_buffer_mutex_};
    capture_event_buffer_ = nullptr;
  }
}

grpc::Status ProducerSideServiceImpl::ReceiveCommandsAndSendEvents(
    ::grpc::ServerContext* context,
    ::grpc::ServerReaderWriter< ::orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                                ::orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream) {
  LOG("A CaptureEventProducer has connected calling ReceiveCommandsAndSendEvents");

  {
    absl::MutexLock lock{&server_contexts_mutex_};
    server_contexts_.emplace(context);
  }

  // This keeps whether we are still waiting for an AllEventsSent message at the end of a capture.
  // It starts as true as we aren't yet waiting for such message when the connection is established.
  // Note that this will also be protected by service_state_mutex_.
  bool all_events_sent_received = true;

  std::atomic<bool> receive_events_thread_exited = false;

  // This thread is responsible for writing on stream, and specifically for
  // sending StartCaptureCommands and StopCaptureCommands to the connected producer.
  std::thread send_commands_thread{&ProducerSideServiceImpl::SendCommandsThread,
                                   this,
                                   context,
                                   stream,
                                   &all_events_sent_received,
                                   &receive_events_thread_exited};

  // This thread is responsible for reading from stream, and specifically for
  // receiving CaptureEvents and AllEventsSent messages.
  std::thread receive_events_thread{&ProducerSideServiceImpl::ReceiveEventsThread, this, context,
                                    stream, &all_events_sent_received};
  receive_events_thread.join();

  // When receive_events_thread exits because stream->Read(&request) fails,
  // it means that the producer has disconnected: ask send_commands_thread to exit, too.
  receive_events_thread_exited = true;
  send_commands_thread.join();

  {
    absl::MutexLock lock{&server_contexts_mutex_};
    server_contexts_.erase(context);
  }

  LOG("Finished handling ReceiveCommandsAndSendEvents for a CaptureEventProducer");
  return grpc::Status::OK;
}

static bool SendStartCaptureCommand(
    grpc::ServerContext* context,
    grpc::ServerReaderWriter<orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                             orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream) {
  orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse command;
  command.mutable_start_capture_command();
  if (!stream->Write(command)) {
    ERROR("Sending StartCaptureCommand to CaptureEventProducer");
    LOG("Terminating call to ReceiveCommandsAndSendEvents as Write failed");
    // Cause Read in ReceiveEventsThread to also fail if for some reason it hasn't already.
    context->TryCancel();
    return false;
  }
  LOG("Sent StartCaptureCommand to CaptureEventProducer");
  return true;
}

static bool SendStopCaptureCommand(
    grpc::ServerContext* context,
    grpc::ServerReaderWriter<orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                             orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream) {
  orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse command;
  command.mutable_stop_capture_command();
  if (!stream->Write(command)) {
    ERROR("Sending StopCaptureCommand to CaptureEventProducer");
    LOG("Terminating call to ReceiveCommandsAndSendEvents as Write failed");
    // Cause Read in ReceiveEventsThread to also fail if for some reason it hasn't already.
    context->TryCancel();
    return false;
  }
  LOG("Sent StopCaptureCommand to CaptureEventProducer");
  return true;
}

static bool SendCaptureFinishedCommand(
    grpc::ServerContext* context,
    grpc::ServerReaderWriter<orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                             orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream) {
  orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse command;
  command.mutable_capture_finished_command();
  if (!stream->Write(command)) {
    ERROR("Sending CaptureFinishedCommand to CaptureEventProducer");
    LOG("Terminating call to ReceiveCommandsAndSendEvents as Write failed");
    // Cause Read in ReceiveEventsThread to also fail if for some reason it hasn't already.
    context->TryCancel();
    return false;
  }
  LOG("Sent CaptureFinishedCommand to CaptureEventProducer");
  return true;
}

void ProducerSideServiceImpl::SendCommandsThread(
    grpc::ServerContext* context,
    grpc::ServerReaderWriter<orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                             orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream,
    bool* all_events_sent_received, std::atomic<bool>* receive_events_thread_exited) {
  // As a result of initializing prev_capture_status to kCaptureFinished,
  // an initial StartCaptureCommand is sent
  // if service_state_.capture_status is actually CaptureStatus::kCaptureStarted,
  // and an initial StopCaptureCommand is sent (with little effect)
  // if service_state_.capture_status is actually CaptureStatus::kCaptureStopping.
  CaptureStatus prev_capture_status = CaptureStatus::kCaptureFinished;

  // This loop keeps track of changes to service_state_.capture_status using
  // conditional critical sections on service_state_mutex_ and updating prev_capture_status,
  // and sends StartCaptureCommands and StopCaptureCommands accordingly.
  // It exits when one of *receive_events_thread_exited and service_state_.exit_requested is true,
  // or when Write fails (because the producer disconnected or because the context was cancelled).
  while (true) {
    // This is set when ReceiveEventsThread has exited. At that point this thread should also exit.
    if (*receive_events_thread_exited) {
      return;
    }

    CaptureStatus curr_capture_status;
    {
      absl::MutexLock lock{&service_state_mutex_};
      if (service_state_.exit_requested) {
        return;
      }

      if (service_state_.capture_status == prev_capture_status) {
        // Wait for service_state_.capture_status to change or for service_state->exit_requested
        // (the next iteration will handle the change).
        // Use a timeout to periodically check (in the next iteration)
        // for *terminate_send_commands_thread, set by ReceiveCommandsAndSendEvents.
        static constexpr absl::Duration kCheckExitSendCommandsThreadInterval = absl::Seconds(1);

        // The three cases in this switch are almost identical, except
        // for the value service_state->capture_status is compared with.
        // The reason for the duplication is that AwaitWithTimeout takes a function pointer, which
        // means that the lambda cannot capture the initial value of service_state_.capture_status.
        switch (service_state_.capture_status) {
          case CaptureStatus::kCaptureStarted: {
            service_state_mutex_.AwaitWithTimeout(absl::Condition(
                                                      +[](ServiceState* service_state) {
                                                        return service_state->exit_requested ||
                                                               service_state->capture_status !=
                                                                   CaptureStatus::kCaptureStarted;
                                                      },
                                                      &service_state_),
                                                  kCheckExitSendCommandsThreadInterval);
          } break;

          case CaptureStatus::kCaptureStopping: {
            service_state_mutex_.AwaitWithTimeout(absl::Condition(
                                                      +[](ServiceState* service_state) {
                                                        return service_state->exit_requested ||
                                                               service_state->capture_status !=
                                                                   CaptureStatus::kCaptureStopping;
                                                      },
                                                      &service_state_),
                                                  kCheckExitSendCommandsThreadInterval);
          } break;

          case CaptureStatus::kCaptureFinished: {
            service_state_mutex_.AwaitWithTimeout(absl::Condition(
                                                      +[](ServiceState* service_state) {
                                                        return service_state->exit_requested ||
                                                               service_state->capture_status !=
                                                                   CaptureStatus::kCaptureFinished;
                                                      },
                                                      &service_state_),
                                                  kCheckExitSendCommandsThreadInterval);
          } break;
        }

        continue;
      }

      // service_state_.capture_status has changed compared to prev_capture_status: handle the
      // change while holding service_state_mutex_ (that also protects all_events_sent_received).
      switch (service_state_.capture_status) {
        case CaptureStatus::kCaptureStarted: {
          ++service_state_.producers_remaining;
          *all_events_sent_received = false;
        } break;

        case CaptureStatus::kCaptureStopping:
          break;

        case CaptureStatus::kCaptureFinished: {
          *all_events_sent_received = true;
        } break;
      }
      curr_capture_status = service_state_.capture_status;
    }  // absl::MutexLock lock{&service_state_mutex_}

    // curr_capture_status now holds the new service_state_.capture_status. Send commands
    // to the producer based on its value and also based on the value of prev_capture_status,
    // in case this thread missed an intermediate change of service_state_.capture_status.
    if (curr_capture_status == CaptureStatus::kCaptureStarted &&
        prev_capture_status == CaptureStatus::kCaptureFinished) {
      if (!SendStartCaptureCommand(context, stream)) {
        return;
      }
    } else if (curr_capture_status == CaptureStatus::kCaptureStarted &&
               prev_capture_status == CaptureStatus::kCaptureStopping) {
      if (!SendCaptureFinishedCommand(context, stream) ||
          !SendStartCaptureCommand(context, stream)) {
        return;
      }

    } else if (curr_capture_status == CaptureStatus::kCaptureStopping &&
               prev_capture_status == CaptureStatus::kCaptureStarted) {
      if (!SendStopCaptureCommand(context, stream)) {
        return;
      }
    } else if (curr_capture_status == CaptureStatus::kCaptureStopping &&
               prev_capture_status == CaptureStatus::kCaptureFinished) {
      if (!SendStartCaptureCommand(context, stream) || !SendStopCaptureCommand(context, stream)) {
        return;
      }

    } else if (curr_capture_status == CaptureStatus::kCaptureFinished &&
               prev_capture_status == CaptureStatus::kCaptureStopping) {
      if (!SendCaptureFinishedCommand(context, stream)) {
        return;
      }
    } else if (curr_capture_status == CaptureStatus::kCaptureFinished &&
               prev_capture_status == CaptureStatus::kCaptureStarted) {
      if (!SendStopCaptureCommand(context, stream) ||
          !SendCaptureFinishedCommand(context, stream)) {
        return;
      }
    } else {
      UNREACHABLE();
    }
    prev_capture_status = curr_capture_status;
  }
}

void ProducerSideServiceImpl::ReceiveEventsThread(
    grpc::ServerContext* /*context*/,
    grpc::ServerReaderWriter<orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                             orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream,
    bool* all_events_sent_received) {
  orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest request;
  while (stream->Read(&request)) {
    {
      absl::MutexLock lock{&service_state_mutex_};
      if (service_state_.exit_requested) {
        break;
      }
    }

    switch (request.event_case()) {
      case orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest::kBufferedCaptureEvents: {
        absl::MutexLock lock{&capture_event_buffer_mutex_};
        // capture_event_buffer_ can be nullptr if a producer sends events while not capturing.
        // Don't log an error in such a case as it could easily spam the logs.
        if (capture_event_buffer_ != nullptr) {
          for (orbit_grpc_protos::CaptureEvent event :
               request.buffered_capture_events().capture_events()) {
            capture_event_buffer_->AddEvent(std::move(event));
          }
        }
      } break;

      case orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest::kAllEventsSent: {
        LOG("Received AllEventsSent from CaptureEventProducer");
        absl::MutexLock lock{&service_state_mutex_};
        switch (service_state_.capture_status) {
          case CaptureStatus::kCaptureStarted: {
            ERROR("CaptureEventProducer sent AllEventsSent while still capturing");
            // Even if we weren't waiting for the AllEventsSent message yet,
            // still keep track of the fact that we have already received it.
            if (!*all_events_sent_received) {
              --service_state_.producers_remaining;
              *all_events_sent_received = true;
            }
          } break;

          case CaptureStatus::kCaptureStopping: {
            // If we were waiting for AllEventsSent, keep track of the fact that we received it.
            if (!*all_events_sent_received) {
              --service_state_.producers_remaining;
              *all_events_sent_received = true;
            }
          } break;

          case CaptureStatus::kCaptureFinished: {
            ERROR("CaptureEventProducer sent AllEventsSent after the capture had finished");
          } break;
        }
      } break;

      case orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest::EVENT_NOT_SET: {
        ERROR("CaptureEventProducer sent EVENT_NOT_SET");
      } break;
    }
  }

  ERROR("Receiving ReceiveCommandsAndSendEventsRequest from CaptureEventProducer");
  {
    absl::MutexLock lock{&service_state_mutex_};
    // Producer has disconnected: treat this as if it had sent all its CaptureEvents.
    if (!*all_events_sent_received &&
        (service_state_.capture_status == CaptureStatus::kCaptureStarted ||
         service_state_.capture_status == CaptureStatus::kCaptureStopping)) {
      --service_state_.producers_remaining;
      *all_events_sent_received = true;
    }
  }
}

}  // namespace orbit_service
<commit_msg>Replace if with switch in ProducerSideServiceImpl::SendCommandsThread<commit_after>// Copyright (c) 2020 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ProducerSideServiceImpl.h"

#include <thread>

#include "OrbitBase/Logging.h"

namespace orbit_service {

void ProducerSideServiceImpl::OnCaptureStartRequested(CaptureEventBuffer* capture_event_buffer) {
  CHECK(capture_event_buffer != nullptr);
  LOG("About to send StartCaptureCommand to CaptureEventProducers (if any)");
  {
    absl::WriterMutexLock lock{&capture_event_buffer_mutex_};
    capture_event_buffer_ = capture_event_buffer;
  }
  {
    absl::MutexLock lock{&service_state_mutex_};
    service_state_.capture_status = CaptureStatus::kCaptureStarted;
  }
}

void ProducerSideServiceImpl::OnCaptureStopRequested() {
  LOG("About to send StopCaptureCommand to CaptureEventProducers (if any)");
  {
    absl::MutexLock lock{&service_state_mutex_};
    service_state_.capture_status = CaptureStatus::kCaptureStopping;

    // Wait (for a limited amount of time) for all producers to send AllEventsSent or to disconnect.
    service_state_mutex_.AwaitWithTimeout(
        absl::Condition(
            +[](ServiceState* service_state) {
              return service_state->producers_remaining == 0 || service_state->exit_requested;
            },
            &service_state_),
        absl::Milliseconds(static_cast<int64_t>(max_wait_for_all_events_sent_ms_)));
    CHECK(service_state_.producers_remaining >= 0);
    if (service_state_.producers_remaining == 0) {
      LOG("All CaptureEventProducers have finished sending their CaptureEvents");
    } else {
      ERROR(
          "Stopped receiving CaptureEvents from CaptureEventProducers "
          "even if not all have sent all their CaptureEvents");
    }
    LOG("About to send CaptureFinishedCommand to CaptureEventProducers (if any)");
    service_state_.capture_status = CaptureStatus::kCaptureFinished;
    service_state_.producers_remaining = 0;
  }

  {
    absl::WriterMutexLock lock{&capture_event_buffer_mutex_};
    capture_event_buffer_ = nullptr;
  }
}

void ProducerSideServiceImpl::OnExitRequest() {
  {
    absl::MutexLock lock{&service_state_mutex_};
    service_state_.exit_requested = true;
  }

  LOG("Attempting to disconnect from CaptureEventProducers as exit was requested");
  {
    absl::MutexLock lock{&server_contexts_mutex_};
    for (grpc::ServerContext* context : server_contexts_) {
      // This should cause blocking Reads on ServerReaderWriter to fail immediately.
      context->TryCancel();
    }
  }

  {
    absl::WriterMutexLock lock{&capture_event_buffer_mutex_};
    capture_event_buffer_ = nullptr;
  }
}

grpc::Status ProducerSideServiceImpl::ReceiveCommandsAndSendEvents(
    ::grpc::ServerContext* context,
    ::grpc::ServerReaderWriter< ::orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                                ::orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream) {
  LOG("A CaptureEventProducer has connected calling ReceiveCommandsAndSendEvents");

  {
    absl::MutexLock lock{&server_contexts_mutex_};
    server_contexts_.emplace(context);
  }

  // This keeps whether we are still waiting for an AllEventsSent message at the end of a capture.
  // It starts as true as we aren't yet waiting for such message when the connection is established.
  // Note that this will also be protected by service_state_mutex_.
  bool all_events_sent_received = true;

  std::atomic<bool> receive_events_thread_exited = false;

  // This thread is responsible for writing on stream, and specifically for
  // sending StartCaptureCommands and StopCaptureCommands to the connected producer.
  std::thread send_commands_thread{&ProducerSideServiceImpl::SendCommandsThread,
                                   this,
                                   context,
                                   stream,
                                   &all_events_sent_received,
                                   &receive_events_thread_exited};

  // This thread is responsible for reading from stream, and specifically for
  // receiving CaptureEvents and AllEventsSent messages.
  std::thread receive_events_thread{&ProducerSideServiceImpl::ReceiveEventsThread, this, context,
                                    stream, &all_events_sent_received};
  receive_events_thread.join();

  // When receive_events_thread exits because stream->Read(&request) fails,
  // it means that the producer has disconnected: ask send_commands_thread to exit, too.
  receive_events_thread_exited = true;
  send_commands_thread.join();

  {
    absl::MutexLock lock{&server_contexts_mutex_};
    server_contexts_.erase(context);
  }

  LOG("Finished handling ReceiveCommandsAndSendEvents for a CaptureEventProducer");
  return grpc::Status::OK;
}

static bool SendStartCaptureCommand(
    grpc::ServerContext* context,
    grpc::ServerReaderWriter<orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                             orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream) {
  orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse command;
  command.mutable_start_capture_command();
  if (!stream->Write(command)) {
    ERROR("Sending StartCaptureCommand to CaptureEventProducer");
    LOG("Terminating call to ReceiveCommandsAndSendEvents as Write failed");
    // Cause Read in ReceiveEventsThread to also fail if for some reason it hasn't already.
    context->TryCancel();
    return false;
  }
  LOG("Sent StartCaptureCommand to CaptureEventProducer");
  return true;
}

static bool SendStopCaptureCommand(
    grpc::ServerContext* context,
    grpc::ServerReaderWriter<orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                             orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream) {
  orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse command;
  command.mutable_stop_capture_command();
  if (!stream->Write(command)) {
    ERROR("Sending StopCaptureCommand to CaptureEventProducer");
    LOG("Terminating call to ReceiveCommandsAndSendEvents as Write failed");
    // Cause Read in ReceiveEventsThread to also fail if for some reason it hasn't already.
    context->TryCancel();
    return false;
  }
  LOG("Sent StopCaptureCommand to CaptureEventProducer");
  return true;
}

static bool SendCaptureFinishedCommand(
    grpc::ServerContext* context,
    grpc::ServerReaderWriter<orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                             orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream) {
  orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse command;
  command.mutable_capture_finished_command();
  if (!stream->Write(command)) {
    ERROR("Sending CaptureFinishedCommand to CaptureEventProducer");
    LOG("Terminating call to ReceiveCommandsAndSendEvents as Write failed");
    // Cause Read in ReceiveEventsThread to also fail if for some reason it hasn't already.
    context->TryCancel();
    return false;
  }
  LOG("Sent CaptureFinishedCommand to CaptureEventProducer");
  return true;
}

void ProducerSideServiceImpl::SendCommandsThread(
    grpc::ServerContext* context,
    grpc::ServerReaderWriter<orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                             orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream,
    bool* all_events_sent_received, std::atomic<bool>* receive_events_thread_exited) {
  // As a result of initializing prev_capture_status to kCaptureFinished,
  // an initial StartCaptureCommand is sent
  // if service_state_.capture_status is actually CaptureStatus::kCaptureStarted,
  // and an initial StopCaptureCommand is sent (with little effect)
  // if service_state_.capture_status is actually CaptureStatus::kCaptureStopping.
  CaptureStatus prev_capture_status = CaptureStatus::kCaptureFinished;

  // This loop keeps track of changes to service_state_.capture_status using
  // conditional critical sections on service_state_mutex_ and updating prev_capture_status,
  // and sends StartCaptureCommands and StopCaptureCommands accordingly.
  // It exits when one of *receive_events_thread_exited and service_state_.exit_requested is true,
  // or when Write fails (because the producer disconnected or because the context was cancelled).
  while (true) {
    // This is set when ReceiveEventsThread has exited. At that point this thread should also exit.
    if (*receive_events_thread_exited) {
      return;
    }

    CaptureStatus curr_capture_status;
    {
      absl::MutexLock lock{&service_state_mutex_};
      if (service_state_.exit_requested) {
        return;
      }

      if (service_state_.capture_status == prev_capture_status) {
        // Wait for service_state_.capture_status to change or for service_state->exit_requested
        // (the next iteration will handle the change).
        // Use a timeout to periodically check (in the next iteration)
        // for *terminate_send_commands_thread, set by ReceiveCommandsAndSendEvents.
        static constexpr absl::Duration kCheckExitSendCommandsThreadInterval = absl::Seconds(1);

        // The three cases in this switch are almost identical, except
        // for the value service_state->capture_status is compared with.
        // The reason for the duplication is that AwaitWithTimeout takes a function pointer, which
        // means that the lambda cannot capture the initial value of service_state_.capture_status.
        switch (service_state_.capture_status) {
          case CaptureStatus::kCaptureStarted: {
            service_state_mutex_.AwaitWithTimeout(absl::Condition(
                                                      +[](ServiceState* service_state) {
                                                        return service_state->exit_requested ||
                                                               service_state->capture_status !=
                                                                   CaptureStatus::kCaptureStarted;
                                                      },
                                                      &service_state_),
                                                  kCheckExitSendCommandsThreadInterval);
          } break;

          case CaptureStatus::kCaptureStopping: {
            service_state_mutex_.AwaitWithTimeout(absl::Condition(
                                                      +[](ServiceState* service_state) {
                                                        return service_state->exit_requested ||
                                                               service_state->capture_status !=
                                                                   CaptureStatus::kCaptureStopping;
                                                      },
                                                      &service_state_),
                                                  kCheckExitSendCommandsThreadInterval);
          } break;

          case CaptureStatus::kCaptureFinished: {
            service_state_mutex_.AwaitWithTimeout(absl::Condition(
                                                      +[](ServiceState* service_state) {
                                                        return service_state->exit_requested ||
                                                               service_state->capture_status !=
                                                                   CaptureStatus::kCaptureFinished;
                                                      },
                                                      &service_state_),
                                                  kCheckExitSendCommandsThreadInterval);
          } break;
        }

        continue;
      }

      // service_state_.capture_status has changed compared to prev_capture_status: handle the
      // change while holding service_state_mutex_ (that also protects all_events_sent_received).
      switch (service_state_.capture_status) {
        case CaptureStatus::kCaptureStarted: {
          ++service_state_.producers_remaining;
          *all_events_sent_received = false;
        } break;

        case CaptureStatus::kCaptureStopping:
          break;

        case CaptureStatus::kCaptureFinished: {
          *all_events_sent_received = true;
        } break;
      }
      curr_capture_status = service_state_.capture_status;
    }  // absl::MutexLock lock{&service_state_mutex_}

    // curr_capture_status now holds the new service_state_.capture_status. Send commands
    // to the producer based on its value and also based on the value of prev_capture_status,
    // in case this thread missed an intermediate change of service_state_.capture_status.
    switch (curr_capture_status) {
      case CaptureStatus::kCaptureStarted: {
        if (prev_capture_status == CaptureStatus::kCaptureFinished) {
          if (!SendStartCaptureCommand(context, stream)) {
            return;
          }
        } else if (prev_capture_status == CaptureStatus::kCaptureStopping) {
          if (!SendCaptureFinishedCommand(context, stream) ||
              !SendStartCaptureCommand(context, stream)) {
            return;
          }
        } else {
          UNREACHABLE();
        }
      } break;

      case CaptureStatus::kCaptureStopping: {
        if (prev_capture_status == CaptureStatus::kCaptureStarted) {
          if (!SendStopCaptureCommand(context, stream)) {
            return;
          }
        } else if (prev_capture_status == CaptureStatus::kCaptureFinished) {
          if (!SendStartCaptureCommand(context, stream) ||
              !SendStopCaptureCommand(context, stream)) {
            return;
          }
        } else {
          UNREACHABLE();
        }
      } break;

      case CaptureStatus::kCaptureFinished:
        if (prev_capture_status == CaptureStatus::kCaptureStopping) {
          if (!SendCaptureFinishedCommand(context, stream)) {
            return;
          }
        } else if (prev_capture_status == CaptureStatus::kCaptureStarted) {
          if (!SendStopCaptureCommand(context, stream) ||
              !SendCaptureFinishedCommand(context, stream)) {
            return;
          }
        } else {
          UNREACHABLE();
        }
        break;
    }

    prev_capture_status = curr_capture_status;
  }
}

void ProducerSideServiceImpl::ReceiveEventsThread(
    grpc::ServerContext* /*context*/,
    grpc::ServerReaderWriter<orbit_grpc_protos::ReceiveCommandsAndSendEventsResponse,
                             orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest>* stream,
    bool* all_events_sent_received) {
  orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest request;
  while (stream->Read(&request)) {
    {
      absl::MutexLock lock{&service_state_mutex_};
      if (service_state_.exit_requested) {
        break;
      }
    }

    switch (request.event_case()) {
      case orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest::kBufferedCaptureEvents: {
        absl::MutexLock lock{&capture_event_buffer_mutex_};
        // capture_event_buffer_ can be nullptr if a producer sends events while not capturing.
        // Don't log an error in such a case as it could easily spam the logs.
        if (capture_event_buffer_ != nullptr) {
          for (orbit_grpc_protos::CaptureEvent event :
               request.buffered_capture_events().capture_events()) {
            capture_event_buffer_->AddEvent(std::move(event));
          }
        }
      } break;

      case orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest::kAllEventsSent: {
        LOG("Received AllEventsSent from CaptureEventProducer");
        absl::MutexLock lock{&service_state_mutex_};
        switch (service_state_.capture_status) {
          case CaptureStatus::kCaptureStarted: {
            ERROR("CaptureEventProducer sent AllEventsSent while still capturing");
            // Even if we weren't waiting for the AllEventsSent message yet,
            // still keep track of the fact that we have already received it.
            if (!*all_events_sent_received) {
              --service_state_.producers_remaining;
              *all_events_sent_received = true;
            }
          } break;

          case CaptureStatus::kCaptureStopping: {
            // If we were waiting for AllEventsSent, keep track of the fact that we received it.
            if (!*all_events_sent_received) {
              --service_state_.producers_remaining;
              *all_events_sent_received = true;
            }
          } break;

          case CaptureStatus::kCaptureFinished: {
            ERROR("CaptureEventProducer sent AllEventsSent after the capture had finished");
          } break;
        }
      } break;

      case orbit_grpc_protos::ReceiveCommandsAndSendEventsRequest::EVENT_NOT_SET: {
        ERROR("CaptureEventProducer sent EVENT_NOT_SET");
      } break;
    }
  }

  ERROR("Receiving ReceiveCommandsAndSendEventsRequest from CaptureEventProducer");
  {
    absl::MutexLock lock{&service_state_mutex_};
    // Producer has disconnected: treat this as if it had sent all its CaptureEvents.
    if (!*all_events_sent_received &&
        (service_state_.capture_status == CaptureStatus::kCaptureStarted ||
         service_state_.capture_status == CaptureStatus::kCaptureStopping)) {
      --service_state_.producers_remaining;
      *all_events_sent_received = true;
    }
  }
}

}  // namespace orbit_service
<|endoftext|>


<commit_before>
#include "Ext.h"

#include <llvm/IR/Function.h>
#include <llvm/IR/TypeBuilder.h>
#include <llvm/IR/IntrinsicInst.h>

//#include <libdevcrypto/SHA3.h>
//#include <libevm/FeeStructure.h>

#include "RuntimeManager.h"
#include "Memory.h"
#include "Type.h"
#include "Endianness.h"

namespace dev
{
namespace eth
{
namespace jit
{

Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
	RuntimeHelper(_runtimeManager),
	m_memoryMan(_memoryMan)
#ifdef __MSCVER
	,
	m_funcs({}),  // The only std::array initialization that works in both Visual Studio & GCC
	m_argAllocas({})
#endif
{
	m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
}

using FuncDesc = std::tuple<char const*, llvm::FunctionType*>;

llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list<llvm::Type*> const& _argsTypes)
{
	return llvm::FunctionType::get(_returnType, llvm::ArrayRef<llvm::Type*>{_argsTypes.begin(), _argsTypes.size()}, false);
}

std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
{
	static std::array<FuncDesc, sizeOf<EnvFunc>::value> descs{{
		FuncDesc{"env_sload",   getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
		FuncDesc{"env_sstore",  getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
		FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})},
		FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
		FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})},
		FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})},
		FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})},
		FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
		FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
		FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})},
	}};

	return descs;
}

llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module)
{
	auto&& desc = getEnvFuncDescs()[static_cast<size_t>(_id)];
	return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module);
}

llvm::Value* Ext::getArgAlloca()
{
	auto& a = m_argAllocas[m_argCounter++];
	if (!a)
	{
		// FIXME: Improve order and names
		InsertPointGuard g{getBuilder()};
		getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI());
		a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg");
	}

	return a;
}

llvm::Value* Ext::byPtr(llvm::Value* _value)
{
	auto a = getArgAlloca();
	getBuilder().CreateStore(_value, a);
	return a;
}

llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args)
{
	auto& func = m_funcs[static_cast<size_t>(_funcId)];
	if (!func)
		func = createFunc(_funcId, getModule());

	m_argCounter = 0;
	return getBuilder().CreateCall(func, {_args.begin(), _args.size()});
}

llvm::Value* Ext::sload(llvm::Value* _index)
{
	auto ret = getArgAlloca();
	createCall(EnvFunc::sload, {getRuntimeManager().getEnvPtr(), byPtr(_index), ret}); // Uses native endianness
	return m_builder.CreateLoad(ret);
}

void Ext::sstore(llvm::Value* _index, llvm::Value* _value)
{
	createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness
}

llvm::Value* Ext::calldataload(llvm::Value* _index)
{
	auto ret = getArgAlloca();
	createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret});
	ret = m_builder.CreateLoad(ret);
	return Endianness::toNative(m_builder, ret);
}

llvm::Value* Ext::balance(llvm::Value* _address)
{
	auto address = Endianness::toBE(m_builder, _address);
	auto ret = getArgAlloca();
	createCall(EnvFunc::balance, {getRuntimeManager().getEnvPtr(), byPtr(address), ret});
	return m_builder.CreateLoad(ret);
}

llvm::Value* Ext::blockhash(llvm::Value* _number)
{
	auto hash = getArgAlloca();
	createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash});
	hash =  m_builder.CreateLoad(hash);
	return Endianness::toNative(getBuilder(), hash);
}

llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize)
{
	auto gas = byPtr(_gas);
	auto ret = getArgAlloca();
	auto begin = m_memoryMan.getBytePtr(_initOff);
	auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size");
	createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret});
	_gas = m_builder.CreateLoad(gas); // Return gas
	llvm::Value* address = m_builder.CreateLoad(ret);
	address = Endianness::toNative(m_builder, address);
	return address;
}

llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress)
{
	auto gas = byPtr(_gas);
	auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress);
	auto inBeg = m_memoryMan.getBytePtr(_inOff);
	auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size");
	auto outBeg = m_memoryMan.getBytePtr(_outOff);
	auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size");
	auto codeAddress = Endianness::toBE(m_builder, _codeAddress);
	auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), gas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)});
	_gas = m_builder.CreateLoad(gas); // Return gas
	return m_builder.CreateZExt(ret, Type::Word, "ret");
}

llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
{
	auto begin = m_memoryMan.getBytePtr(_inOff);
	auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size");
	auto ret = getArgAlloca();
	createCall(EnvFunc::sha3, {begin, size, ret});
	llvm::Value* hash = m_builder.CreateLoad(ret);
	hash = Endianness::toNative(m_builder, hash);
	return hash;
}

MemoryRef Ext::extcode(llvm::Value* _addr)
{
	auto addr = Endianness::toBE(m_builder, _addr);
	auto code = createCall(EnvFunc::extcode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size});
	auto codeSize = m_builder.CreateLoad(m_size);
	auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word);
	return {code, codeSize256};
}

void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics)
{
	auto begin = m_memoryMan.getBytePtr(_memIdx);
	auto size = m_builder.CreateTrunc(_numBytes, Type::Size, "size");
	llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, getArgAlloca(), getArgAlloca(), getArgAlloca(), getArgAlloca()};

	auto topicArgPtr = &args[3];
	for (auto&& topic : _topics)
	{
		if (topic)
			m_builder.CreateStore(Endianness::toBE(m_builder, topic), *topicArgPtr);
		else
			*topicArgPtr = llvm::ConstantPointerNull::get(Type::WordPtr);
		++topicArgPtr;
	}

	createCall(EnvFunc::log, {args[0], args[1], args[2], args[3], args[4], args[5], args[6]});  // TODO: use std::initializer_list<>
}

}
}
}
<commit_msg>Correct std::array intialization (again!)<commit_after>
#include "Ext.h"

#include <llvm/IR/Function.h>
#include <llvm/IR/TypeBuilder.h>
#include <llvm/IR/IntrinsicInst.h>

//#include <libdevcrypto/SHA3.h>
//#include <libevm/FeeStructure.h>

#include "RuntimeManager.h"
#include "Memory.h"
#include "Type.h"
#include "Endianness.h"

namespace dev
{
namespace eth
{
namespace jit
{

Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
	RuntimeHelper(_runtimeManager),
	m_memoryMan(_memoryMan)
{
	m_funcs = decltype(m_funcs)();
	m_argAllocas = decltype(m_argAllocas)();
	m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
}

using FuncDesc = std::tuple<char const*, llvm::FunctionType*>;

llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list<llvm::Type*> const& _argsTypes)
{
	return llvm::FunctionType::get(_returnType, llvm::ArrayRef<llvm::Type*>{_argsTypes.begin(), _argsTypes.size()}, false);
}

std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
{
	static std::array<FuncDesc, sizeOf<EnvFunc>::value> descs{{
		FuncDesc{"env_sload",   getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
		FuncDesc{"env_sstore",  getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
		FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})},
		FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
		FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})},
		FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})},
		FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})},
		FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
		FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
		FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})},
	}};

	return descs;
}

llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module)
{
	auto&& desc = getEnvFuncDescs()[static_cast<size_t>(_id)];
	return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module);
}

llvm::Value* Ext::getArgAlloca()
{
	auto& a = m_argAllocas[m_argCounter++];
	if (!a)
	{
		// FIXME: Improve order and names
		InsertPointGuard g{getBuilder()};
		getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI());
		a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg");
	}

	return a;
}

llvm::Value* Ext::byPtr(llvm::Value* _value)
{
	auto a = getArgAlloca();
	getBuilder().CreateStore(_value, a);
	return a;
}

llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args)
{
	auto& func = m_funcs[static_cast<size_t>(_funcId)];
	if (!func)
		func = createFunc(_funcId, getModule());

	m_argCounter = 0;
	return getBuilder().CreateCall(func, {_args.begin(), _args.size()});
}

llvm::Value* Ext::sload(llvm::Value* _index)
{
	auto ret = getArgAlloca();
	createCall(EnvFunc::sload, {getRuntimeManager().getEnvPtr(), byPtr(_index), ret}); // Uses native endianness
	return m_builder.CreateLoad(ret);
}

void Ext::sstore(llvm::Value* _index, llvm::Value* _value)
{
	createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness
}

llvm::Value* Ext::calldataload(llvm::Value* _index)
{
	auto ret = getArgAlloca();
	createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret});
	ret = m_builder.CreateLoad(ret);
	return Endianness::toNative(m_builder, ret);
}

llvm::Value* Ext::balance(llvm::Value* _address)
{
	auto address = Endianness::toBE(m_builder, _address);
	auto ret = getArgAlloca();
	createCall(EnvFunc::balance, {getRuntimeManager().getEnvPtr(), byPtr(address), ret});
	return m_builder.CreateLoad(ret);
}

llvm::Value* Ext::blockhash(llvm::Value* _number)
{
	auto hash = getArgAlloca();
	createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash});
	hash =  m_builder.CreateLoad(hash);
	return Endianness::toNative(getBuilder(), hash);
}

llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize)
{
	auto gas = byPtr(_gas);
	auto ret = getArgAlloca();
	auto begin = m_memoryMan.getBytePtr(_initOff);
	auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size");
	createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret});
	_gas = m_builder.CreateLoad(gas); // Return gas
	llvm::Value* address = m_builder.CreateLoad(ret);
	address = Endianness::toNative(m_builder, address);
	return address;
}

llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress)
{
	auto gas = byPtr(_gas);
	auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress);
	auto inBeg = m_memoryMan.getBytePtr(_inOff);
	auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size");
	auto outBeg = m_memoryMan.getBytePtr(_outOff);
	auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size");
	auto codeAddress = Endianness::toBE(m_builder, _codeAddress);
	auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), gas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)});
	_gas = m_builder.CreateLoad(gas); // Return gas
	return m_builder.CreateZExt(ret, Type::Word, "ret");
}

llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
{
	auto begin = m_memoryMan.getBytePtr(_inOff);
	auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size");
	auto ret = getArgAlloca();
	createCall(EnvFunc::sha3, {begin, size, ret});
	llvm::Value* hash = m_builder.CreateLoad(ret);
	hash = Endianness::toNative(m_builder, hash);
	return hash;
}

MemoryRef Ext::extcode(llvm::Value* _addr)
{
	auto addr = Endianness::toBE(m_builder, _addr);
	auto code = createCall(EnvFunc::extcode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size});
	auto codeSize = m_builder.CreateLoad(m_size);
	auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word);
	return {code, codeSize256};
}

void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics)
{
	auto begin = m_memoryMan.getBytePtr(_memIdx);
	auto size = m_builder.CreateTrunc(_numBytes, Type::Size, "size");
	llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, getArgAlloca(), getArgAlloca(), getArgAlloca(), getArgAlloca()};

	auto topicArgPtr = &args[3];
	for (auto&& topic : _topics)
	{
		if (topic)
			m_builder.CreateStore(Endianness::toBE(m_builder, topic), *topicArgPtr);
		else
			*topicArgPtr = llvm::ConstantPointerNull::get(Type::WordPtr);
		++topicArgPtr;
	}

	createCall(EnvFunc::log, {args[0], args[1], args[2], args[3], args[4], args[5], args[6]});  // TODO: use std::initializer_list<>
}

}
}
}
<|endoftext|>


<commit_before>/*=========================================================================

 Program:   Visualization Toolkit
 Module:    vtkFieldDataSerializer.cxx

 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
 All rights reserved.
 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notice for more information.

 =========================================================================*/
#include "vtkFieldDataSerializer.h"
#include "vtkObjectFactory.h"
#include "vtkFieldData.h"
#include "vtkDataArray.h"
#include "vtkIdList.h"
#include "vtkStructuredData.h"
#include "vtkStringArray.h"
#include "vtkIntArray.h"
#include "vtkMultiProcessStream.h"

#include <cassert> // For assert()
#include <cstring> // For memcpy

vtkStandardNewMacro(vtkFieldDataSerializer);

//------------------------------------------------------------------------------
vtkFieldDataSerializer::vtkFieldDataSerializer()
{

}

//------------------------------------------------------------------------------
vtkFieldDataSerializer::~vtkFieldDataSerializer()
{

}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::SerializeMetaData(
    vtkFieldData *fieldData, vtkMultiProcessStream& bytestream)
{
  if( fieldData == NULL )
    {
    vtkGenericWarningMacro("Field data is NULL!");
    return;
    }

  // STEP 0: Write the number of arrays
  bytestream << fieldData->GetNumberOfArrays();

  // STEP 1: Loop through each array and write the metadata
  for( int array=0; array < fieldData->GetNumberOfArrays(); ++array )
    {
    vtkDataArray *dataArray = fieldData->GetArray( array );
    assert("pre: data array should not be NULL!" && (dataArray != NULL));

    int dataType  = dataArray->GetDataType();
    int numComp   = dataArray->GetNumberOfComponents();
    int numTuples = dataArray->GetNumberOfTuples();

    // serialize array information
    bytestream << dataType << numTuples << numComp;
    bytestream << std::string( dataArray->GetName() );
    } // END for all arrays
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::DeserializeMetaData(
    vtkMultiProcessStream& bytestream,
    vtkStringArray *names,
    vtkIntArray *datatypes,
    vtkIntArray *dimensions)
{
  if( bytestream.Empty() )
    {
    vtkGenericWarningMacro("ByteStream is empty");
    return;
    }

  if( (names == NULL) || (datatypes == NULL) || (dimensions == NULL) )
    {
    vtkGenericWarningMacro(
        "ERROR: caller must pre-allocation names/datatypes/dimensions!");
    return;
    }

  // STEP 0: Extract the number of arrays
  int NumberOfArrays;
  bytestream >> NumberOfArrays;
  if( NumberOfArrays == 0 )
    {
    return;
    }

  // STEP 1: Allocate output data-structures
  names->SetNumberOfValues(NumberOfArrays);
  datatypes->SetNumberOfValues(NumberOfArrays);
  dimensions->SetNumberOfComponents(2);
  dimensions->SetNumberOfTuples(NumberOfArrays);

  std::string *namesPtr = static_cast<std::string*>(names->GetVoidPointer(0));
  int *datatypesPtr     = static_cast<int*>(datatypes->GetVoidPointer(0));
  int *dimensionsPtr    = static_cast<int*>(dimensions->GetVoidPointer(0));

  // STEP 2: Extract metadata for each array in corresponding output arrays
  for( int arrayIdx=0; arrayIdx < NumberOfArrays; ++arrayIdx )
    {
    bytestream >> datatypesPtr[ arrayIdx ] >> dimensionsPtr[arrayIdx*2] >>
                  dimensionsPtr[arrayIdx*2+1] >> namesPtr[ arrayIdx ];
    } // END for all arrays
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::Serialize(
    vtkFieldData *fieldData, vtkMultiProcessStream& bytestream)
{
  if( fieldData == NULL )
    {
    vtkGenericWarningMacro("Field data is NULL!");
    return;
    }

  // STEP 0: Write the number of arrays
  bytestream << fieldData->GetNumberOfArrays();

  if( fieldData->GetNumberOfArrays() == 0 )
    {
    return;
    }

  // STEP 1: Loop through each array and serialize its metadata
  for( int array=0; array < fieldData->GetNumberOfArrays(); ++array )
    {
    vtkDataArray *dataArray = fieldData->GetArray( array );
    vtkFieldDataSerializer::SerializeDataArray( dataArray, bytestream );
    } // END for all arrays
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::SerializeTuples(
    vtkIdList *tupleIds, vtkFieldData *fieldData,
    vtkMultiProcessStream& bytestream )
{
  if( fieldData == NULL )
   {
   vtkGenericWarningMacro("Field data is NULL!");
   return;
   }

  // STEP 0: Write the number of arrays
  bytestream << fieldData->GetNumberOfArrays();

  if( fieldData->GetNumberOfArrays() == 0 )
    {
    return;
    }

  // STEP 1: Loop through each array, extract the data on the selected tuples
  // and serialize it
  for( int array=0; array < fieldData->GetNumberOfArrays(); ++array )
   {
   vtkDataArray *dataArray = fieldData->GetArray( array );

   // STEP 2: For each array extract only the selected tuples, i.e., a subset
   vtkDataArray *subSet = NULL;
   subSet = vtkFieldDataSerializer::ExtractSelectedTuples(tupleIds,dataArray);
   assert("pre: subset array is NULL!" && (subSet != NULL) );

   // STEP 3: Serialize only a subset of the data
   vtkFieldDataSerializer::SerializeDataArray( subSet, bytestream );
   subSet->Delete();
   } // END for all arrays

}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::SerializeSubExtent(
    int subext[6], int gridExtent[6], vtkFieldData *fieldData,
    vtkMultiProcessStream& bytestream)
{
  if( fieldData == NULL )
   {
   vtkGenericWarningMacro("Field data is NULL!");
   return;
   }

  // STEP 0: Write the number of arrays
  bytestream << fieldData->GetNumberOfArrays();

  if( fieldData->GetNumberOfArrays() == 0 )
    {
    return;
    }

  // STEP 1: Loop through each array, extract the data within the subext
  // and serialize it
  for( int array=0; array < fieldData->GetNumberOfArrays(); ++array )
    {
    vtkDataArray *dataArray = fieldData->GetArray( array );

    // STEP 2: Extract the data within the requested sub-extent
    vtkDataArray *subSet = NULL;
    subSet = vtkFieldDataSerializer::ExtractSubExtentData(
        subext,gridExtent,dataArray);
    assert("pre: subset array is NULL!" && (subSet != NULL) );

    // STEP 3: Serialize only a subset of the data
    vtkFieldDataSerializer::SerializeDataArray( subSet, bytestream );
    subSet->Delete();
    } // END for all arrays

}

//------------------------------------------------------------------------------
vtkDataArray* vtkFieldDataSerializer::ExtractSubExtentData(
    int subext[6], int gridExtent[6], vtkDataArray *inputDataArray )
{
  if( inputDataArray == NULL )
    {
    vtkGenericWarningMacro("input data array is NULL!");
    return NULL;
    }

  // STEP 0: Acquire structured data description, i.e, XY_PLANE, XYZ_GRID etc.
  int description = vtkStructuredData::GetDataDescriptionFromExtent(gridExtent);

  // STEP 1: Allocate subset array
  vtkDataArray *subSetArray =
      vtkDataArray::CreateDataArray( inputDataArray->GetDataType() );
  subSetArray->SetName( inputDataArray->GetName() );
  subSetArray->SetNumberOfComponents( inputDataArray->GetNumberOfComponents());
  subSetArray->SetNumberOfTuples(
      vtkStructuredData::GetNumberOfNodes(subext,description));

  int ijk[3];
  for( ijk[0]=subext[0]; ijk[0] <= subext[1]; ++ijk[0] )
    {
    for( ijk[1]=subext[2]; ijk[1] <= subext[3]; ++ijk[1] )
      {
      for( ijk[2]=subext[4]; ijk[2] <= subext[5]; ++ijk[2] )
        {
        // Compute the source index from the grid extent. Note, this could be
        // a cell index if the incoming gridExtent and subext are cell extents.
        vtkIdType sourceIdx =
            vtkStructuredData::ComputePointIdForExtent(
                gridExtent,ijk,description);
        assert("pre: source index is out-of-bounds" &&
               (sourceIdx >= 0) &&
               (sourceIdx < inputDataArray->GetNumberOfTuples()));

        // Compute the target index in the subset array. Likewise, this could be
        // either a cell index or a node index depending on what gridExtent or
        // subext represent.
        vtkIdType targetIdx =
            vtkStructuredData::ComputePointIdForExtent(
                subext,ijk,description);
        assert("pre: target index is out-of-bounds" &&
               (targetIdx >= 0) &&
               (targetIdx < subSetArray->GetNumberOfTuples()));

        subSetArray->SetTuple( targetIdx, sourceIdx, inputDataArray );
        } // END for all k
      } // END for all j
    } // END for all i

  return(subSetArray);
}

//------------------------------------------------------------------------------
vtkDataArray* vtkFieldDataSerializer::ExtractSelectedTuples(
    vtkIdList *tupleIds, vtkDataArray *inputDataArray )
{
  vtkDataArray *subSetArray =
      vtkDataArray::CreateDataArray( inputDataArray->GetDataType() );
  subSetArray->SetName( inputDataArray->GetName() );
  subSetArray->SetNumberOfComponents( inputDataArray->GetNumberOfComponents());
  subSetArray->SetNumberOfTuples(tupleIds->GetNumberOfIds());

  vtkIdType idx = 0;
  for( ; idx < tupleIds->GetNumberOfIds(); ++idx )
    {
    vtkIdType tupleIdx = tupleIds->GetId(idx);
    assert("pre: tuple ID is out-of bounds" &&
           (tupleIdx >= 0) && (tupleIdx < inputDataArray->GetNumberOfTuples()));

    subSetArray->SetTuple( idx, tupleIdx, inputDataArray );
    } // END for all tuples to extract
  return( subSetArray );
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::SerializeDataArray(
    vtkDataArray *dataArray, vtkMultiProcessStream& bytestream)
{
  if( dataArray == NULL )
    {
    vtkGenericWarningMacro("data array is NULL!");
    return;
    }

  // STEP 0: Serialize array information
  int dataType  = dataArray->GetDataType();
  int numComp   = dataArray->GetNumberOfComponents();
  int numTuples = dataArray->GetNumberOfTuples();

  // serialize array information
  bytestream << dataType << numTuples << numComp;
  bytestream << std::string( dataArray->GetName() );

  // STEP 1: Push the raw data into the bytestream
  // TODO: Add more cases for more datatypes here (?)
  unsigned int size = numComp*numTuples;
  switch( dataArray->GetDataType() )
    {
    case VTK_FLOAT:
       bytestream.Push(static_cast<float*>(dataArray->GetVoidPointer(0)),size);
       break;
     case VTK_DOUBLE:
       bytestream.Push(static_cast<double*>(dataArray->GetVoidPointer(0)),size);
       break;
     case VTK_INT:
       bytestream.Push(static_cast<int*>(dataArray->GetVoidPointer(0)),size);
       break;
     default:
       assert("ERROR: cannot serialize data of given type" && false);
       cerr << "Canot serialize data of type="
            << dataArray->GetDataType() << endl;

    }
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::Deserialize(
    vtkMultiProcessStream& bytestream, vtkFieldData *fieldData)
{
  if( fieldData == NULL )
    {
    vtkGenericWarningMacro("FieldData is NULL!");
    return;
    }

  if( bytestream.Empty() )
    {
    vtkGenericWarningMacro("Bytestream is empty!");
    return;
    }

  // STEP 0: Get the number of arrays
  int numberOfArrays = 0;
  bytestream >> numberOfArrays;

  if( numberOfArrays == 0 )
    {
    return;
    }

  // STEP 1: Loop and deserialize each array
  for( int array=0; array < numberOfArrays; ++array )
    {
    vtkDataArray *dataArray = NULL;
    vtkFieldDataSerializer::DeserializeDataArray( bytestream,dataArray );
    assert("post: deserialized data array should not be NULL!" &&
            (dataArray != NULL));
    fieldData->AddArray( dataArray );
    dataArray->Delete();
    } // END for all arrays
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::DeserializeDataArray(
    vtkMultiProcessStream& bytestream,
    vtkDataArray *&dataArray)
{
  if( bytestream.Empty() )
    {
    vtkGenericWarningMacro("Bytestream is empty!");
    return;
    }

  // STEP 0: Deserialize array information
  int dataType, numTuples, numComp;
  std::string name;

  bytestream >> dataType >> numTuples >> numComp >> name;

  // STEP 1: Construct vtkDataArray object
  dataArray = vtkDataArray::CreateDataArray( dataType );
  dataArray->SetNumberOfComponents( numComp );
  dataArray->SetNumberOfTuples( numTuples );
  dataArray->SetName( name.c_str() );

  // STEP 2: Extract raw data to vtkDataArray
  // TODO: Add more cases for more datatypes here (?)
  unsigned int size = 0;
  switch( dataType )
    {
    case VTK_FLOAT:
      {
      float *data = NULL;
      bytestream.Pop(data,size);
      assert("pre: deserialized raw data array is NULL" && (data != NULL) );

      float *dataArrayPtr = static_cast<float*>(dataArray->GetVoidPointer(0));
      assert("pre: data array pointer is NULL!" && (dataArrayPtr != NULL) );

      std::memcpy(dataArrayPtr,data,size*sizeof(float));
      delete [] data;
      }
      break;
    case VTK_DOUBLE:
      {
      double *data = NULL;
      bytestream.Pop(data,size);
      assert("pre: deserialized raw data array is NULL" && (data != NULL) );

      double *dataArrayPtr = static_cast<double*>(dataArray->GetVoidPointer(0));
      assert("pre: data array pointer is NULL!" && (dataArrayPtr != NULL) );

      std::memcpy(dataArrayPtr,data,size*sizeof(double));
      delete [] data;
      }
      break;
    case VTK_INT:
      {
      int *data = NULL;
      bytestream.Pop(data,size);
      assert("pre: deserialized raw data array is NULL" && (data != NULL) );

      int *dataArrayPtr = static_cast<int*>(dataArray->GetVoidPointer(0));
      assert("pre: data array pointer is NULL!" && (dataArrayPtr != NULL) );

      std::memcpy(dataArrayPtr,data,size*sizeof(int));
      delete [] data;
      }
      break;
    default:
      assert("ERROR: cannot serialize data of given type" && false);
      cerr << "Canot serialize data of type="
           << dataArray->GetDataType() << endl;
    }
}
<commit_msg>ENH: Support for VtkIdType & enhancements<commit_after>/*=========================================================================

 Program:   Visualization Toolkit
 Module:    vtkFieldDataSerializer.cxx

 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
 All rights reserved.
 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notice for more information.

 =========================================================================*/
#include "vtkFieldDataSerializer.h"
#include "vtkObjectFactory.h"
#include "vtkFieldData.h"
#include "vtkDataArray.h"
#include "vtkIdList.h"
#include "vtkStructuredData.h"
#include "vtkStringArray.h"
#include "vtkIntArray.h"
#include "vtkMultiProcessStream.h"

#include <cassert> // For assert()
#include <cstring> // For memcpy

vtkStandardNewMacro(vtkFieldDataSerializer);

//------------------------------------------------------------------------------
vtkFieldDataSerializer::vtkFieldDataSerializer()
{

}

//------------------------------------------------------------------------------
vtkFieldDataSerializer::~vtkFieldDataSerializer()
{

}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::SerializeMetaData(
    vtkFieldData *fieldData, vtkMultiProcessStream& bytestream)
{
  if( fieldData == NULL )
    {
    vtkGenericWarningMacro("Field data is NULL!");
    return;
    }

  // STEP 0: Write the number of arrays
  bytestream << fieldData->GetNumberOfArrays();

  // STEP 1: Loop through each array and write the metadata
  for( int array=0; array < fieldData->GetNumberOfArrays(); ++array )
    {
    vtkDataArray *dataArray = fieldData->GetArray( array );
    assert("pre: data array should not be NULL!" && (dataArray != NULL));

    int dataType  = dataArray->GetDataType();
    int numComp   = dataArray->GetNumberOfComponents();
    int numTuples = dataArray->GetNumberOfTuples();

    // serialize array information
    bytestream << dataType << numTuples << numComp;
    bytestream << std::string( dataArray->GetName() );
    } // END for all arrays
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::DeserializeMetaData(
    vtkMultiProcessStream& bytestream,
    vtkStringArray *names,
    vtkIntArray *datatypes,
    vtkIntArray *dimensions)
{
  if( bytestream.Empty() )
    {
    vtkGenericWarningMacro("ByteStream is empty");
    return;
    }

  if( (names == NULL) || (datatypes == NULL) || (dimensions == NULL) )
    {
    vtkGenericWarningMacro(
        "ERROR: caller must pre-allocation names/datatypes/dimensions!");
    return;
    }

  // STEP 0: Extract the number of arrays
  int NumberOfArrays;
  bytestream >> NumberOfArrays;
  if( NumberOfArrays == 0 )
    {
    return;
    }

  // STEP 1: Allocate output data-structures
  names->SetNumberOfValues(NumberOfArrays);
  datatypes->SetNumberOfValues(NumberOfArrays);
  dimensions->SetNumberOfComponents(2);
  dimensions->SetNumberOfTuples(NumberOfArrays);

  std::string *namesPtr = static_cast<std::string*>(names->GetVoidPointer(0));
  int *datatypesPtr     = static_cast<int*>(datatypes->GetVoidPointer(0));
  int *dimensionsPtr    = static_cast<int*>(dimensions->GetVoidPointer(0));

  // STEP 2: Extract metadata for each array in corresponding output arrays
  for( int arrayIdx=0; arrayIdx < NumberOfArrays; ++arrayIdx )
    {
    bytestream >> datatypesPtr[ arrayIdx ] >> dimensionsPtr[arrayIdx*2] >>
                  dimensionsPtr[arrayIdx*2+1] >> namesPtr[ arrayIdx ];
    } // END for all arrays
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::Serialize(
    vtkFieldData *fieldData, vtkMultiProcessStream& bytestream)
{
  if( fieldData == NULL )
    {
    vtkGenericWarningMacro("Field data is NULL!");
    return;
    }

  // STEP 0: Write the number of arrays
  bytestream << fieldData->GetNumberOfArrays();

  if( fieldData->GetNumberOfArrays() == 0 )
    {
    return;
    }

  // STEP 1: Loop through each array and serialize its metadata
  for( int array=0; array < fieldData->GetNumberOfArrays(); ++array )
    {
    vtkDataArray *dataArray = fieldData->GetArray( array );
    vtkFieldDataSerializer::SerializeDataArray( dataArray, bytestream );
    } // END for all arrays
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::SerializeTuples(
    vtkIdList *tupleIds, vtkFieldData *fieldData,
    vtkMultiProcessStream& bytestream )
{
  if( fieldData == NULL )
   {
   vtkGenericWarningMacro("Field data is NULL!");
   return;
   }

  // STEP 0: Write the number of arrays
  bytestream << fieldData->GetNumberOfArrays();

  if( fieldData->GetNumberOfArrays() == 0 )
    {
    return;
    }

  // STEP 1: Loop through each array, extract the data on the selected tuples
  // and serialize it
  for( int array=0; array < fieldData->GetNumberOfArrays(); ++array )
   {
   vtkDataArray *dataArray = fieldData->GetArray( array );

   // STEP 2: For each array extract only the selected tuples, i.e., a subset
   vtkDataArray *subSet = NULL;
   subSet = vtkFieldDataSerializer::ExtractSelectedTuples(tupleIds,dataArray);
   assert("pre: subset array is NULL!" && (subSet != NULL) );

   // STEP 3: Serialize only a subset of the data
   vtkFieldDataSerializer::SerializeDataArray( subSet, bytestream );
   subSet->Delete();
   } // END for all arrays

}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::SerializeSubExtent(
    int subext[6], int gridExtent[6], vtkFieldData *fieldData,
    vtkMultiProcessStream& bytestream)
{
  if( fieldData == NULL )
   {
   vtkGenericWarningMacro("Field data is NULL!");
   return;
   }

  // STEP 0: Write the number of arrays
  bytestream << fieldData->GetNumberOfArrays();

  if( fieldData->GetNumberOfArrays() == 0 )
    {
    return;
    }

  // STEP 1: Loop through each array, extract the data within the subext
  // and serialize it
  for( int array=0; array < fieldData->GetNumberOfArrays(); ++array )
    {
    vtkDataArray *dataArray = fieldData->GetArray( array );

    // STEP 2: Extract the data within the requested sub-extent
    vtkDataArray *subSet = NULL;
    subSet = vtkFieldDataSerializer::ExtractSubExtentData(
        subext,gridExtent,dataArray);
    assert("pre: subset array is NULL!" && (subSet != NULL) );

    // STEP 3: Serialize only a subset of the data
    vtkFieldDataSerializer::SerializeDataArray( subSet, bytestream );
    subSet->Delete();
    } // END for all arrays

}

//------------------------------------------------------------------------------
vtkDataArray* vtkFieldDataSerializer::ExtractSubExtentData(
    int subext[6], int gridExtent[6], vtkDataArray *inputDataArray )
{
  if( inputDataArray == NULL )
    {
    vtkGenericWarningMacro("input data array is NULL!");
    return NULL;
    }

  // STEP 0: Acquire structured data description, i.e, XY_PLANE, XYZ_GRID etc.
  int description = vtkStructuredData::GetDataDescriptionFromExtent(gridExtent);

  // STEP 1: Allocate subset array
  vtkDataArray *subSetArray =
      vtkDataArray::CreateDataArray( inputDataArray->GetDataType() );
  subSetArray->SetName( inputDataArray->GetName() );
  subSetArray->SetNumberOfComponents( inputDataArray->GetNumberOfComponents());
  subSetArray->SetNumberOfTuples(
      vtkStructuredData::GetNumberOfNodes(subext,description));

  int ijk[3];
  for( ijk[0]=subext[0]; ijk[0] <= subext[1]; ++ijk[0] )
    {
    for( ijk[1]=subext[2]; ijk[1] <= subext[3]; ++ijk[1] )
      {
      for( ijk[2]=subext[4]; ijk[2] <= subext[5]; ++ijk[2] )
        {
        // Compute the source index from the grid extent. Note, this could be
        // a cell index if the incoming gridExtent and subext are cell extents.
        vtkIdType sourceIdx =
            vtkStructuredData::ComputePointIdForExtent(
                gridExtent,ijk,description);
        assert("pre: source index is out-of-bounds" &&
               (sourceIdx >= 0) &&
               (sourceIdx < inputDataArray->GetNumberOfTuples()));

        // Compute the target index in the subset array. Likewise, this could be
        // either a cell index or a node index depending on what gridExtent or
        // subext represent.
        vtkIdType targetIdx =
            vtkStructuredData::ComputePointIdForExtent(
                subext,ijk,description);
        assert("pre: target index is out-of-bounds" &&
               (targetIdx >= 0) &&
               (targetIdx < subSetArray->GetNumberOfTuples()));

        subSetArray->SetTuple( targetIdx, sourceIdx, inputDataArray );
        } // END for all k
      } // END for all j
    } // END for all i

  return(subSetArray);
}

//------------------------------------------------------------------------------
vtkDataArray* vtkFieldDataSerializer::ExtractSelectedTuples(
    vtkIdList *tupleIds, vtkDataArray *inputDataArray )
{
  vtkDataArray *subSetArray =
      vtkDataArray::CreateDataArray( inputDataArray->GetDataType() );
  subSetArray->SetName( inputDataArray->GetName() );
  subSetArray->SetNumberOfComponents( inputDataArray->GetNumberOfComponents());
  subSetArray->SetNumberOfTuples(tupleIds->GetNumberOfIds());

  vtkIdType idx = 0;
  for( ; idx < tupleIds->GetNumberOfIds(); ++idx )
    {
    vtkIdType tupleIdx = tupleIds->GetId(idx);
    assert("pre: tuple ID is out-of bounds" &&
           (tupleIdx >= 0) && (tupleIdx < inputDataArray->GetNumberOfTuples()));

    subSetArray->SetTuple( idx, tupleIdx, inputDataArray );
    } // END for all tuples to extract
  return( subSetArray );
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::SerializeDataArray(
    vtkDataArray *dataArray, vtkMultiProcessStream& bytestream)
{
  if( dataArray == NULL )
    {
    vtkGenericWarningMacro("data array is NULL!");
    return;
    }

  // STEP 0: Serialize array information
  int dataType  = dataArray->GetDataType();
  int numComp   = dataArray->GetNumberOfComponents();
  int numTuples = dataArray->GetNumberOfTuples();

  // serialize array information
  bytestream << dataType << numTuples << numComp;
  bytestream << std::string( dataArray->GetName() );

  // STEP 1: Push the raw data into the bytestream
  // TODO: Add more cases for more datatypes here (?)
  unsigned int size = numComp*numTuples;
  switch( dataArray->GetDataType() )
    {
    case VTK_FLOAT:
       bytestream.Push(static_cast<float*>(dataArray->GetVoidPointer(0)),size);
       break;
     case VTK_DOUBLE:
       bytestream.Push(static_cast<double*>(dataArray->GetVoidPointer(0)),size);
       break;
     case VTK_INT:
       bytestream.Push(static_cast<int*>(dataArray->GetVoidPointer(0)),size);
       break;
     case VTK_ID_TYPE:
       bytestream.Push(
           static_cast<vtkIdType*>(dataArray->GetVoidPointer(0)),size);
       break;
     default:
       assert("ERROR: cannot serialize data of given type" && false);
       cerr << "Canot serialize data of type="
            << dataArray->GetDataType() << endl;

    }
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::Deserialize(
    vtkMultiProcessStream& bytestream, vtkFieldData *fieldData)
{
  if( fieldData == NULL )
    {
    vtkGenericWarningMacro("FieldData is NULL!");
    return;
    }

  if( bytestream.Empty() )
    {
    vtkGenericWarningMacro("Bytestream is empty!");
    return;
    }

  // STEP 0: Get the number of arrays
  int numberOfArrays = 0;
  bytestream >> numberOfArrays;

  if( numberOfArrays == 0 )
    {
    return;
    }

  // STEP 1: Loop and deserialize each array
  for( int array=0; array < numberOfArrays; ++array )
    {
    vtkDataArray *dataArray = NULL;
    vtkFieldDataSerializer::DeserializeDataArray( bytestream,dataArray );
    assert("post: deserialized data array should not be NULL!" &&
            (dataArray != NULL));
    fieldData->AddArray( dataArray );
    dataArray->Delete();
    } // END for all arrays
}

//------------------------------------------------------------------------------
void vtkFieldDataSerializer::DeserializeDataArray(
    vtkMultiProcessStream& bytestream,
    vtkDataArray *&dataArray)
{
  if( bytestream.Empty() )
    {
    vtkGenericWarningMacro("Bytestream is empty!");
    return;
    }

  // STEP 0: Deserialize array information
  int dataType, numTuples, numComp;
  std::string name;

  bytestream >> dataType >> numTuples >> numComp >> name;
  assert("pre: numComp >= 1" && (numComp >= 1) );

  // STEP 1: Construct vtkDataArray object
  dataArray = vtkDataArray::CreateDataArray( dataType );
  dataArray->SetNumberOfComponents( numComp );
  dataArray->SetNumberOfTuples( numTuples );
  dataArray->SetName( name.c_str() );

  // STEP 2: Extract raw data to vtkDataArray
  // TODO: Add more cases for more datatypes here (?)
  unsigned int size = numTuples*numComp;
  void* rawPtr = dataArray->GetVoidPointer(0);
  assert("pre: raw pointer is NULL!" && (rawPtr != NULL) );
  switch( dataType )
    {
    case VTK_FLOAT:
      {
      float* data = static_cast<float*>(rawPtr);
      bytestream.Pop(data,size);
      }
      break;
    case VTK_DOUBLE:
      {
      double *data = static_cast<double*>(rawPtr);
      bytestream.Pop(data,size);
      }
      break;
    case VTK_INT:
      {
      int *data = static_cast<int*>(rawPtr);
      bytestream.Pop(data,size);
      }
      break;
    case VTK_ID_TYPE:
      {
      vtkIdType* data = static_cast<vtkIdType*>(rawPtr);
      bytestream.Pop(data,size);
      }
      break;
    default:
      assert("ERROR: cannot serialize data of given type" && false);
      cerr << "Canot serialize data of type="
           << dataArray->GetDataType() << endl;
    }
}
<|endoftext|>


<commit_before>#include <assert.h>
#include <chrono>
#include <list>
#include <algorithm>

#include "narf/client/console.h"

narf::Console *narf::console;

struct narf::ClientConsoleImpl {

	ClientConsoleImpl() : editState() {}

	~ClientConsoleImpl() {
		if (textBuffer) {
			delete textBuffer;
		}
		if (editBuffer) {
			delete textBuffer;
		}
	}

	narf::font::Font *font;
	uint32_t lineHeight;
	uint32_t x;
	uint32_t y;
	uint32_t width;
	uint32_t height;
	uint32_t paddingLeft;
	uint32_t scrollback;
	narf::font::TextBuffer *textBuffer;
	narf::font::TextBuffer *editBuffer;
	std::vector<std::string> text;
	narf::TextEditor editState;
	bool editing;
	narf::ClientConsole::CursorShape cursorShape;
	narf::gl::Context* gl;
};

narf::ClientConsole::ClientConsole() {
	impl = new ClientConsoleImpl();
	impl->font = nullptr;
	impl->textBuffer = nullptr;
	impl->editBuffer = nullptr;
	impl->paddingLeft = 5; // TODO: make this configurable
	impl->cursorShape = CursorShape::Default;
	impl->gl = nullptr;
	impl->scrollback = 0;
	last_blink = std::chrono::system_clock::now();
	blink_cursor = true;
	blink_rate = 600;  // Milliseconds
}

uint32_t narf::ClientConsole::getHeightInLines() {
	// TODO: fix rendering so this is not so crazy
	return (impl->height - impl->lineHeight * 3 / 2) / impl->lineHeight;
}

void narf::ClientConsole::pageUp() {
	impl->scrollback += getHeightInLines();
	impl->scrollback = std::min(impl->scrollback, impl->text.size() - getHeightInLines());
}

void narf::ClientConsole::pageDown() {
	impl->scrollback -= std::min(getHeightInLines(), impl->scrollback);
}

void narf::ClientConsole::scrollHome() {
	impl->scrollback = impl->text.size() - getHeightInLines();
}

void narf::ClientConsole::scrollEnd() {
	impl->scrollback = 0;
}

void narf::ClientConsole::setGLContext(narf::gl::Context* gl) {
	impl->gl = gl;
}

narf::TextEditor &narf::ClientConsole::getTextEditor() {
	return impl->editState;
}

narf::font::Font* narf::ClientConsole::getFont() const {
	return impl->font;
}

void narf::ClientConsole::setFont(narf::font::Font *font) {
	impl->font = font;
	impl->lineHeight = (uint32_t)font->height();

	if (impl->textBuffer) {
		impl->textBuffer->setFont(font);
	} else {
		impl->textBuffer = new narf::font::TextBuffer(*impl->gl, font);
	}

	if (impl->editBuffer) {
		impl->editBuffer->setFont(font);
	} else {
		impl->editBuffer = new narf::font::TextBuffer(*impl->gl, font);
	}

	// re-render current text
	update();
}

void narf::ClientConsole::setLocation(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
	if (impl->x != x ||
		impl->y != y ||
		impl->width != width ||
		impl->height != height) {
		impl->x = x;
		impl->y = y;
		impl->width = width;
		impl->height = height;
		update();
	}
}

void narf::ClientConsole::setEditState(bool editing) {
	if (editing && !impl->editing) {
		impl->editState.last_edited = std::chrono::system_clock::now();
	}
	impl->editing = editing;
	update();
}

void narf::ClientConsole::setCursorShape(CursorShape shape) {
	impl->cursorShape = shape;
}

void narf::ClientConsole::update() {
	auto textX = static_cast<float>(impl->x + impl->paddingLeft);

	if (impl->textBuffer) {
		impl->textBuffer->clear();

		uint32_t y = impl->y + impl->lineHeight * 3 / 2;
		auto scrollLines = impl->scrollback;
		for (auto iter = impl->text.rbegin(); iter != impl->text.rend(); ++iter) {
			if (scrollLines > 0) {
				scrollLines--;
				continue;
			}

			if (y + impl->lineHeight >= impl->height) {
				break;
			}

			auto textY = static_cast<float>(y);
			impl->textBuffer->print(*iter, textX + 1, textY - 1, narf::Color(0.0f, 0.0f, 0.0f, 1.0f));
			impl->textBuffer->print(*iter, textX, textY, narf::Color(1.0f, 1.0f, 1.0f, 1.0f));
			y += impl->lineHeight;
		}
	}

	if (impl->editBuffer) {
		impl->editBuffer->clear();

		auto editY = static_cast<float>(impl->y + impl->lineHeight / 2);
		impl->editBuffer->print(impl->editState.getString(), textX + 1, editY - 1, narf::Color(0.0f, 0.0f, 0.0f, 1.0f));
		impl->editBuffer->print(impl->editState.getString(), textX, editY, narf::Color(1.0f, 1.0f, 1.0f, 1.0f));
	}
}

void narf::ClientConsole::render() {
	if (impl->textBuffer) {

		// draw console background
		auto x1 = float(impl->x);
		auto y1 = float(impl->y);
		auto x2 = float(impl->x + impl->width);
		auto y2 = float(impl->y + impl->height);

		glPushAttrib(GL_ALL_ATTRIB_BITS);
		glColor4f(0.5f, 0.5f, 0.5f, 0.7f);
		glBegin(GL_QUADS);
		glVertex2f(x1, y1);
		glVertex2f(x1, y2);
		glVertex2f(x2, y2);
		glVertex2f(x2, y1);
		glEnd();
		glPopAttrib();

		impl->textBuffer->render();
	}
	if (impl->editBuffer && impl->editing) {
		impl->editBuffer->render();

		// draw cursor
		auto now = std::chrono::system_clock::now();
		if (std::chrono::duration_cast<std::chrono::milliseconds>(now - impl->editState.last_edited).count() < 100) {
			blink_cursor = true;
			last_blink = now;
		} else if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_blink).count() > blink_rate) {
			last_blink = now;
			blink_cursor = !blink_cursor;
		}
		if (blink_cursor) {
			auto editY = static_cast<float>(impl->y + impl->lineHeight / 2);

			glPushAttrib(GL_ALL_ATTRIB_BITS);
			glDisable(GL_TEXTURE_2D);
			glColor4f(1.0f, 1.0f, 1.0f, 0.7f);

			if (impl->cursorShape == CursorShape::Caret) {
				auto x1 = float(impl->x + impl->paddingLeft) + impl->editBuffer->width(impl->editState.getString(), impl->editState.cursor) - 0.5f;
				auto y1 = editY - 1.0f;

				glLineWidth(2);
				glBegin(GL_LINE_STRIP);
				glVertex2f(x1 - 4, y1 - 2);
				glVertex2f(x1, y1);
				glVertex2f(x1 + 4, y1 - 2);
				glEnd();
			} else if (impl->cursorShape == CursorShape::Line) {
				auto x1 = float(impl->x + impl->paddingLeft) + impl->editBuffer->width(impl->editState.getString(), impl->editState.cursor);
				auto y1 = editY - 1.0f;
				auto x2 = x1 + 2.0f;
				auto y2 = y1 + float(impl->lineHeight) - 1.0f;

				glBegin(GL_QUADS);
				glVertex2f(x1, y1);
				glVertex2f(x1, y2);
				glVertex2f(x2, y2);
				glVertex2f(x2, y1);
				glEnd();
			}
			glPopAttrib();
		}
	}
}

narf::ClientConsole::~ClientConsole() {
	delete impl;
}

void narf::ClientConsole::println(const std::string &s) {
	impl->text.push_back(s);
	scrollEnd();
	update();

	// also print to stdout for now
	printf("%s\n", s.c_str());
}

std::string narf::ClientConsole::pollInput() {
	return impl->editState.getString();
}
<commit_msg>client: fix type conversion warnings and errors<commit_after>#include <assert.h>
#include <chrono>
#include <list>
#include <algorithm>

#include "narf/client/console.h"

narf::Console *narf::console;

struct narf::ClientConsoleImpl {

	ClientConsoleImpl() : editState() {}

	~ClientConsoleImpl() {
		if (textBuffer) {
			delete textBuffer;
		}
		if (editBuffer) {
			delete textBuffer;
		}
	}

	narf::font::Font *font;
	uint32_t lineHeight;
	uint32_t x;
	uint32_t y;
	uint32_t width;
	uint32_t height;
	uint32_t paddingLeft;
	uint32_t scrollback;
	narf::font::TextBuffer *textBuffer;
	narf::font::TextBuffer *editBuffer;
	std::vector<std::string> text;
	narf::TextEditor editState;
	bool editing;
	narf::ClientConsole::CursorShape cursorShape;
	narf::gl::Context* gl;
};

narf::ClientConsole::ClientConsole() {
	impl = new ClientConsoleImpl();
	impl->font = nullptr;
	impl->textBuffer = nullptr;
	impl->editBuffer = nullptr;
	impl->paddingLeft = 5; // TODO: make this configurable
	impl->cursorShape = CursorShape::Default;
	impl->gl = nullptr;
	impl->scrollback = 0;
	last_blink = std::chrono::system_clock::now();
	blink_cursor = true;
	blink_rate = 600;  // Milliseconds
}

uint32_t narf::ClientConsole::getHeightInLines() {
	// TODO: fix rendering so this is not so crazy
	return (impl->height - impl->lineHeight * 3 / 2) / impl->lineHeight;
}

void narf::ClientConsole::pageUp() {
	impl->scrollback += getHeightInLines();
	impl->scrollback = std::min(impl->scrollback, (uint32_t)impl->text.size() - getHeightInLines());
}

void narf::ClientConsole::pageDown() {
	impl->scrollback -= std::min(getHeightInLines(), impl->scrollback);
}

void narf::ClientConsole::scrollHome() {
	impl->scrollback = (uint32_t)impl->text.size() - getHeightInLines();
}

void narf::ClientConsole::scrollEnd() {
	impl->scrollback = 0;
}

void narf::ClientConsole::setGLContext(narf::gl::Context* gl) {
	impl->gl = gl;
}

narf::TextEditor &narf::ClientConsole::getTextEditor() {
	return impl->editState;
}

narf::font::Font* narf::ClientConsole::getFont() const {
	return impl->font;
}

void narf::ClientConsole::setFont(narf::font::Font *font) {
	impl->font = font;
	impl->lineHeight = (uint32_t)font->height();

	if (impl->textBuffer) {
		impl->textBuffer->setFont(font);
	} else {
		impl->textBuffer = new narf::font::TextBuffer(*impl->gl, font);
	}

	if (impl->editBuffer) {
		impl->editBuffer->setFont(font);
	} else {
		impl->editBuffer = new narf::font::TextBuffer(*impl->gl, font);
	}

	// re-render current text
	update();
}

void narf::ClientConsole::setLocation(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
	if (impl->x != x ||
		impl->y != y ||
		impl->width != width ||
		impl->height != height) {
		impl->x = x;
		impl->y = y;
		impl->width = width;
		impl->height = height;
		update();
	}
}

void narf::ClientConsole::setEditState(bool editing) {
	if (editing && !impl->editing) {
		impl->editState.last_edited = std::chrono::system_clock::now();
	}
	impl->editing = editing;
	update();
}

void narf::ClientConsole::setCursorShape(CursorShape shape) {
	impl->cursorShape = shape;
}

void narf::ClientConsole::update() {
	auto textX = static_cast<float>(impl->x + impl->paddingLeft);

	if (impl->textBuffer) {
		impl->textBuffer->clear();

		uint32_t y = impl->y + impl->lineHeight * 3 / 2;
		auto scrollLines = impl->scrollback;
		for (auto iter = impl->text.rbegin(); iter != impl->text.rend(); ++iter) {
			if (scrollLines > 0) {
				scrollLines--;
				continue;
			}

			if (y + impl->lineHeight >= impl->height) {
				break;
			}

			auto textY = static_cast<float>(y);
			impl->textBuffer->print(*iter, textX + 1, textY - 1, narf::Color(0.0f, 0.0f, 0.0f, 1.0f));
			impl->textBuffer->print(*iter, textX, textY, narf::Color(1.0f, 1.0f, 1.0f, 1.0f));
			y += impl->lineHeight;
		}
	}

	if (impl->editBuffer) {
		impl->editBuffer->clear();

		auto editY = static_cast<float>(impl->y + impl->lineHeight / 2);
		impl->editBuffer->print(impl->editState.getString(), textX + 1, editY - 1, narf::Color(0.0f, 0.0f, 0.0f, 1.0f));
		impl->editBuffer->print(impl->editState.getString(), textX, editY, narf::Color(1.0f, 1.0f, 1.0f, 1.0f));
	}
}

void narf::ClientConsole::render() {
	if (impl->textBuffer) {

		// draw console background
		auto x1 = float(impl->x);
		auto y1 = float(impl->y);
		auto x2 = float(impl->x + impl->width);
		auto y2 = float(impl->y + impl->height);

		glPushAttrib(GL_ALL_ATTRIB_BITS);
		glColor4f(0.5f, 0.5f, 0.5f, 0.7f);
		glBegin(GL_QUADS);
		glVertex2f(x1, y1);
		glVertex2f(x1, y2);
		glVertex2f(x2, y2);
		glVertex2f(x2, y1);
		glEnd();
		glPopAttrib();

		impl->textBuffer->render();
	}
	if (impl->editBuffer && impl->editing) {
		impl->editBuffer->render();

		// draw cursor
		auto now = std::chrono::system_clock::now();
		if (std::chrono::duration_cast<std::chrono::milliseconds>(now - impl->editState.last_edited).count() < 100) {
			blink_cursor = true;
			last_blink = now;
		} else if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_blink).count() > blink_rate) {
			last_blink = now;
			blink_cursor = !blink_cursor;
		}
		if (blink_cursor) {
			auto editY = static_cast<float>(impl->y + impl->lineHeight / 2);

			glPushAttrib(GL_ALL_ATTRIB_BITS);
			glDisable(GL_TEXTURE_2D);
			glColor4f(1.0f, 1.0f, 1.0f, 0.7f);

			if (impl->cursorShape == CursorShape::Caret) {
				auto x1 = float(impl->x + impl->paddingLeft) + impl->editBuffer->width(impl->editState.getString(), impl->editState.cursor) - 0.5f;
				auto y1 = editY - 1.0f;

				glLineWidth(2);
				glBegin(GL_LINE_STRIP);
				glVertex2f(x1 - 4, y1 - 2);
				glVertex2f(x1, y1);
				glVertex2f(x1 + 4, y1 - 2);
				glEnd();
			} else if (impl->cursorShape == CursorShape::Line) {
				auto x1 = float(impl->x + impl->paddingLeft) + impl->editBuffer->width(impl->editState.getString(), impl->editState.cursor);
				auto y1 = editY - 1.0f;
				auto x2 = x1 + 2.0f;
				auto y2 = y1 + float(impl->lineHeight) - 1.0f;

				glBegin(GL_QUADS);
				glVertex2f(x1, y1);
				glVertex2f(x1, y2);
				glVertex2f(x2, y2);
				glVertex2f(x2, y1);
				glEnd();
			}
			glPopAttrib();
		}
	}
}

narf::ClientConsole::~ClientConsole() {
	delete impl;
}

void narf::ClientConsole::println(const std::string &s) {
	impl->text.push_back(s);
	scrollEnd();
	update();

	// also print to stdout for now
	printf("%s\n", s.c_str());
}

std::string narf::ClientConsole::pollInput() {
	return impl->editState.getString();
}
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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 <net/tcp/write_queue.hpp>

using namespace net::tcp;

Write_queue::Write_queue(WriteCallback cb)
  : current_(0),
    offset_(0),
    acked_(0),
    on_write_(cb)
{}

void Write_queue::advance(size_t bytes)
{
  auto& buf = nxt();

  offset_ += bytes;

  debug2("<WriteQueue> Advance: bytes=%u off=%u rem=%u\n",
    bytes, offset_, (buf->size() - offset_));

  if(offset_ == buf->size())
  {
    current_++;
    offset_ = 0;

    if(on_write_)
      on_write_(buf->size());

    debug("<WriteQueue> Advance: Done (%u) current++ [%u] sz=%u\n",
      buf->size(), current_, q.size());
  }
}

void Write_queue::acknowledge(size_t bytes)
{
  debug2("<WriteQueue> Acknowledge %u bytes, ack=%u\n", bytes, acked_);
  while(bytes and !q.empty())
  {
    auto& buf = una();
    assert(buf->size() >= acked_);
    // remaining
    const auto rem = buf->size() - acked_;

    // if everything or more is acked
    if(bytes >= rem)
    {
      // subtract the bytes acked
      bytes -= rem;
      // reset acked
      acked_ = 0;
      // pop and subtract index
      q.pop_front();
      current_--;

      debug("<WriteQueue> Acknowledge done, current-- [%u] sz=%u\n", current_, q.size());
    }
    else
    {
      // add to acked
      acked_ += bytes;
      bytes = 0;
    }
  }
}

void Write_queue::reset()
{
  if(offset_ > 0 and on_write_ != nullptr)
    on_write_(offset_);

  q.clear();
  current_ = 0;
  debug("<WriteQueue::reset> Reset\n");
}

uint32_t Write_queue::bytes_remaining() const
{
  if(current_ >= q.size()) return 0;

  uint32_t n = nxt()->size() - offset_;

  for(auto i = current_ + 1; i < q.size(); ++i)
    n += q.at(i)->size();

  return n;
}

uint32_t Write_queue::bytes_unacknowledged() const
{
  if(q.empty()) return 0;

  uint32_t n = una()->size() - acked_;

  for(uint32_t i = 1; i < q.size(); ++i)
    n += q.at(i)->size();

  return n;
}

__attribute__((weak))
int Write_queue::deserialize_from(void*) { return 0; }
__attribute__((weak))
int  Write_queue::serialize_to(void*) const { return 0; }
<commit_msg>tcp: Assert on size to avoid overshooting buffer<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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 <net/tcp/write_queue.hpp>

using namespace net::tcp;

Write_queue::Write_queue(WriteCallback cb)
  : current_(0),
    offset_(0),
    acked_(0),
    on_write_(cb)
{}

void Write_queue::advance(size_t bytes)
{
  auto& buf = nxt();

  offset_ += bytes;
  assert(offset_ <= buf->size());

  debug2("<WriteQueue> Advance: bytes=%u off=%u rem=%u\n",
    bytes, offset_, (buf->size() - offset_));

  if(offset_ == buf->size())
  {
    current_++;
    offset_ = 0;

    if(on_write_)
      on_write_(buf->size());

    debug("<WriteQueue> Advance: Done (%u) current++ [%u] sz=%u\n",
      buf->size(), current_, q.size());
  }
}

void Write_queue::acknowledge(size_t bytes)
{
  debug2("<WriteQueue> Acknowledge %u bytes, ack=%u\n", bytes, acked_);
  while(bytes and !q.empty())
  {
    auto& buf = una();
    assert(buf->size() >= acked_);
    // remaining
    const auto rem = buf->size() - acked_;

    // if everything or more is acked
    if(bytes >= rem)
    {
      // subtract the bytes acked
      bytes -= rem;
      // reset acked
      acked_ = 0;
      // pop and subtract index
      q.pop_front();
      current_--;

      debug("<WriteQueue> Acknowledge done, current-- [%u] sz=%u\n", current_, q.size());
    }
    else
    {
      // add to acked
      acked_ += bytes;
      bytes = 0;
    }
  }
}

void Write_queue::reset()
{
  if(offset_ > 0 and on_write_ != nullptr)
    on_write_(offset_);

  q.clear();
  current_ = 0;
  debug("<WriteQueue::reset> Reset\n");
}

uint32_t Write_queue::bytes_remaining() const
{
  if(current_ >= q.size()) return 0;

  uint32_t n = nxt()->size() - offset_;

  for(auto i = current_ + 1; i < q.size(); ++i)
    n += q.at(i)->size();

  return n;
}

uint32_t Write_queue::bytes_unacknowledged() const
{
  if(q.empty()) return 0;

  uint32_t n = una()->size() - acked_;

  for(uint32_t i = 1; i < q.size(); ++i)
    n += q.at(i)->size();

  return n;
}

__attribute__((weak))
int Write_queue::deserialize_from(void*) { return 0; }
__attribute__((weak))
int  Write_queue::serialize_to(void*) const { return 0; }
<|endoftext|>


<commit_before>#pragma once

// Use this helper macro to create a plugin creation method in your plugin implementation &
// your base process. In the base process, the signature of the method will be used to lookup
// the createPlugin() in the plugin shared library.
#define MAKE_PLUGIN_CREATION_METHOD(ConcretePluginImpl) \
extern "C" { \
    void* createPlugin(void* service) \
    { \
        return new ConcretePluginImpl(service); \
    } \
}\
<commit_msg>Introducing a deleter into the plugin macro.<commit_after>#pragma once

// Use this helper macro to create a plugin creation method in your plugin implementation &
// your base process. In the base process, the signature of the method will be used to lookup
// the createPlugin() in the plugin shared library.
#define MAKE_PLUGIN_CREATION_METHOD(ConcretePluginImpl) \
extern "C" { \
    void* createPlugin(void* service) \
    { \
        return new ConcretePluginImpl(service); \
    } \
\
\
    void deletePlugin(void* plugin) \
    { \
        delete plugin; \
    } \
}\
<|endoftext|>