<commit_before>// Copyright (C) 2015 sails Authors.
// All rights reserved.
//
// Official git repository and contact information can be found at
// https://github.com/sails/sails and http://www.sailsxu.com/.
//
// Filename: client_rpc_channel.cc
//
// Author: sailsxu <sailsxu@gmail.com>
// Created: 2015-04-23 16:03:07

#include "src/rpc_channel.h"
#include <stdio.h>
#include <limits.h>
#include <assert.h>
#include <iostream>
#include <sstream>
#include "sails/base/thread_queue.h"
#include "sails/net/connector.h"
#include "src/saf_packet.pb.h"
#include "src/saf_const.h"
#include "src/version.h"
#include "google/protobuf/message.h"
#include "google/protobuf/descriptor.h"

using namespace google::protobuf;  // NOLINT

// 注册销毁protobuf
bool HasRegisterDestoryProtobuffer = false;
void destory_protobuf() {
  google::protobuf::ShutdownProtobufLibrary();
}
void HandleSigpipe(int sig) {
}

namespace sails {

RpcChannelImp::RpcChannelImp(string ip, int port):
    ip(ip), port(port) {
  connector = new net::Connector();
  request_list = new base::ThreadQueue<RequestPacket*>();
  assert(connector->connect(this->ip.c_str(), this->port, true));
  sn = 0;
  stop = false;
  keeplive = false;
  recv_thread = new std::thread(recv_response, this);
  send_thread = new std::thread(send_request, this);
  if (!HasRegisterDestoryProtobuffer) {
    HasRegisterDestoryProtobuffer = true;
    atexit(destory_protobuf);
  }
  isbreak = false;
  // 当连接关闭时，还在发数据，会导致sigpipe信号
  sigpipe_action.sa_handler = HandleSigpipe;
  sigemptyset(&sigpipe_action.sa_mask);
  sigpipe_action.sa_flags = 0;
  sigaction(SIGPIPE, &sigpipe_action, NULL);

  timeout = 10;
  timer = new base::Timer(1);
  timer->init(RpcChannelImp::check_call_timeout, this, 1);
  now = time(NULL);
}

RpcChannelImp::~RpcChannelImp() {
  if (!stop) {
    stop = true;
    shutdown(connector->get_connector_fd(), 2);
    connector->close();
    recv_thread->join();
    send_thread->join();
  }
  if (recv_thread != NULL) {
    delete recv_thread;
  }

  if (send_thread != NULL) {
    delete send_thread;
  }
  if (connector != NULL) {
    delete connector;
    connector = NULL;
  }
  for (auto session : ticketManager) {
    if (session.second != NULL) {
      delete session.second;
    }
  }
  if (request_list != NULL) {
    delete request_list;
    request_list = NULL;
  }
}

void RpcChannelImp::CallMethod(const MethodDescriptor* method,
                               RpcController *controller,
                               const Message *request,
                               Message *response,
                               Closure *done) {
  if (done != NULL) {  // 异步
    this->async_all(method, controller, request, response, done);
  } else {  // 同步
    if (this->sync_call(method, controller, request, response) != 0) {
      printf("sync call error\n");
    }
  }
}

sails::ResponsePacket* RpcChannelImp::parser(
    net::Connector *connector) {

  if (connector->readable() < sizeof(int)) {
    return NULL;
  }
  const char* buffer = connector->peek();
  int packetLen = *(reinterpret_cast<const int*>(buffer));
  if (connector->readable() < packetLen + sizeof(int)) {
    return NULL;
  }
  // printf("parse packet len:%d\n", packetLen);

  ResponsePacket* response = new ResponsePacket();
  if (response->ParseFromArray(buffer+sizeof(int), packetLen)) {
    connector->retrieve(packetLen + sizeof(int));
    return response;
  } else {
    // 出错
    delete response;
    connector->retrieve(connector->readable());
  }

  return NULL;
}

void RpcChannelImp::async_all(const google::protobuf::MethodDescriptor *method,
                 google::protobuf::RpcController* ,
                 const google::protobuf::Message* request,
                 google::protobuf::Message* response,
               google::protobuf::Closure* done) {
  if (stop || isbreak) {
    done->Run();
    return;
  }
  std::unique_lock<std::mutex> locker(request_mutex);
  sn++;
  RequestPacket *packet = new RequestPacket();
  packet->set_version(VERSION_MAJOR*1000+VERSION_MINOR*100+VERSION_PATCH);
  packet->set_type(MessageType::RPC_REQUEST);
  packet->set_sn(sn);
  packet->set_servicename(method->service()->name());
  packet->set_funcname(method->name());
  packet->mutable_detail()->PackFrom(*request);
  packet->set_timeout(timeout);

  ticketManager[sn] = new TicketSession(sn, response, done);
  ticketManager[sn]->calltime = now;
  if (!request_list->push_back(packet)) {
    perror("async call too fast");
    delete ticketManager[sn];
    ticketManager[sn] = NULL;
  }
}

int RpcChannelImp::sync_call(const google::protobuf::MethodDescriptor *method,
                             google::protobuf::RpcController *,
                             const google::protobuf::Message *request,
                             google::protobuf::Message *response) {
  if (stop || isbreak) {
    return -1;
  }
  std::unique_lock<std::mutex> locker(request_mutex);
  sn++;
  RequestPacket *packet = new RequestPacket();
  packet->set_version(VERSION_MAJOR*1000+VERSION_MINOR*100+VERSION_PATCH);
  packet->set_type(MessageType::RPC_REQUEST);
  packet->set_sn(sn);
  packet->set_servicename(method->service()->name());
  packet->set_funcname(method->name());
  packet->mutable_detail()->PackFrom(*request);
  packet->set_timeout(timeout);

  ticketManager[sn] = new TicketSession(sn, response, NULL);
  ticketManager[sn]->calltime = now;
  request_list->push_back(packet);

  // wait notify
  while (ticketManager[sn]->errcode == 1) {
    ticketManager[sn]->notify.wait(locker);
  }
  int errorcode = ticketManager[sn]->errcode;
  delete ticketManager[sn];
  ticketManager.erase(sn);
  return errorcode;
}

void RpcChannelImp::send_request(RpcChannelImp* channel) {
  while (!channel->stop) {
    RequestPacket *request = NULL;
    static int empty_request = 0;
    channel->request_list->pop_front(request, 100);
    if (request != NULL) {
      empty_request = 0;
      std::string data = request->SerializeAsString();
      int len = data.length();
      channel->connector->write(reinterpret_cast<char*>(&len), sizeof(int));
      channel->connector->write(data.c_str(), data.length());
      if (channel->connector->send() > 0) {
        delete request;
      } else {
        // 0表示连接关闭，小于0表示出错
        // 这里不主动设置stop的标识，由recv_response来设置，这样可以由它来
        // 处理后续回调和通知
        channel->reset_ticket();
        delete request;
      }
    } else {
      empty_request++;
      // 超过20次，也就是5s没有发送过数据，则发送一个心跳包
      if (empty_request > 50) {
        empty_request = 0;
        if (!channel->keeplive) {
          continue;
        }
        RequestPacket heartbeat;
        heartbeat.set_version(
            VERSION_MAJOR*1000+VERSION_MINOR*100+VERSION_PATCH);
        heartbeat.set_type(MessageType::PING);
        heartbeat.set_sn(0);
        heartbeat.set_servicename("");
        heartbeat.set_funcname("");
        std::string data = heartbeat.SerializeAsString();
        int len = data.length();
        channel->connector->write(reinterpret_cast<char*>(&len), sizeof(int));
        channel->connector->write(data.c_str(), data.length());
        if (channel->connector->send() > 0) {
          // 成功
        } else {
          // 失败
        }
      }
    }
  }
}

void RpcChannelImp::recv_response(RpcChannelImp* channel) {
  while (!channel->stop) {
    // 为了结束时防止阻塞到这里不能返回，通过调用close使它返回
    int n = channel->connector->read();
    if (n > 0) {
      bool continueParse = false;
      do {
        continueParse = false;
        if (channel->connector->readable() == 0) {
          break;  // 解析一次完成
        }
        ResponsePacket *resp = RpcChannelImp::parser(channel->connector);
        if (resp != NULL) {
          if (resp->ret() == ErrorCode::ERR_SUCCESS) {
            if (resp->type() == MessageType::PING) {
              delete(resp);
              continue;
            }
            std::unique_lock<std::mutex> locker(channel->request_mutex);
            if (channel->ticketManager[resp->sn()] != NULL) {
              resp->detail().UnpackTo(
                  channel->ticketManager[resp->sn()]->response);
              channel->ticketManager[resp->sn()]->errcode = 0;
              if (channel->ticketManager[resp->sn()]->done != NULL) {
                // 异步，那么就在这里调用
                channel->ticketManager[resp->sn()]->done->Run();
                delete channel->ticketManager[resp->sn()];
                channel->ticketManager.erase(resp->sn());
              } else {
                // 同步，通知调用线程
                channel->ticketManager[resp->sn()]->notify.notify_all();
              }
              continueParse = true;
            }
          } else {
            char msg[50];
            snprintf(msg, sizeof(msg), "get a response for error_code %d",
                     resp->ret());
            perror(msg);
          }
          delete(resp);
        } else {
          // 接收的数据不够，等待再次接收后再解析
          continueParse = false;
        }
      } while (continueParse);
    } else {  // 0表示连接被关闭, 小于0，出错
      // perror("recv");
      // printf("read N:%d\n", n);
      // 由于response是一个自定义的结构
      // 所以没有errcode之类的标识，这里就只能由用户自己通过response没有
      // 设置来得知是出错了
      if (n == -1 && errno == EAGAIN) {  // 可能被中断了,重新接收
      } else {  // 出错
        channel->isbreak = true;
        channel->reset_ticket();
        if (channel->keeplive && !channel->stop) {  // 连接关闭了
          // 重连
          while (!channel->stop) {
            sleep(2);
            channel->connector->close();
            if (channel->connector->connect(
                    channel->ip.c_str(), channel->port, true)) {
              channel->isbreak = false;
              break;
            } else {
            }
          }
        }
      }
    }
  }
}

void RpcChannelImp::reset_ticket() {
  std::unique_lock<std::mutex> locker(request_mutex);
  for (auto& ticket : ticketManager) {
    if (ticket.second != NULL) {
      ticket.second->errcode = -1;
      if (ticket.second->done == NULL) {  // 是同步，通知主线程返回
        ticketManager[ticket.first]->notify.notify_all();
      } else {  // 异步
        ticket.second->done->Run();
        delete ticket.second;
        ticket.second = NULL;
      }
    }
  }
}

void RpcChannelImp::check_call_timeout(void * data) {
  if (data != NULL) {
    RpcChannelImp* channel = reinterpret_cast<RpcChannelImp*>(data);
    if (channel->timeout <= 0) {
      return;
    }
    channel->now = time(NULL);
    std::unique_lock<std::mutex> locker(channel->request_mutex);
    for (auto iter = channel->ticketManager.begin();
         iter != channel->ticketManager.end(); ) {
      // 超时
      TicketSession* session = iter->second;
      if (session != NULL
          && channel->now - session->calltime > channel->timeout) {
        session->errcode = -3;
        if (session->done == NULL) {  // 是同步，通知主线程返回
          session->notify.notify_all();
          iter++;
        } else {  // 异步
          session->done->Run();
          delete session;
          iter->second = NULL;
          iter = channel->ticketManager.erase(iter);
        }
      } else {
        iter++;
      }
    }
  }
}

}  // namespace sails
<commit_msg>update<commit_after>// Copyright (C) 2015 sails Authors.
// All rights reserved.
//
// Official git repository and contact information can be found at
// https://github.com/sails/sails and http://www.sailsxu.com/.
//
// Filename: client_rpc_channel.cc
//
// Author: sailsxu <sailsxu@gmail.com>
// Created: 2015-04-23 16:03:07

#include "src/rpc_channel.h"
#include <stdio.h>
#include <limits.h>
#include <assert.h>
#include <iostream>
#include <sstream>
#include "sails/base/thread_queue.h"
#include "sails/net/connector.h"
#include "src/saf_packet.pb.h"
#include "src/saf_const.h"
#include "src/version.h"
#include "google/protobuf/message.h"
#include "google/protobuf/descriptor.h"

using namespace google::protobuf;  // NOLINT

// 注册销毁protobuf
bool HasRegisterDestoryProtobuffer = false;
void destory_protobuf() {
  google::protobuf::ShutdownProtobufLibrary();
}
void HandleSigpipe(int sig) {
}

namespace sails {

RpcChannelImp::RpcChannelImp(string ip, int port):
    ip(ip), port(port) {
  connector = new net::Connector();
  request_list = new base::ThreadQueue<RequestPacket*>();
  assert(connector->connect(this->ip.c_str(), this->port, true));
  sn = 0;
  stop = false;
  keeplive = false;
  recv_thread = new std::thread(recv_response, this);
  send_thread = new std::thread(send_request, this);
  if (!HasRegisterDestoryProtobuffer) {
    HasRegisterDestoryProtobuffer = true;
    atexit(destory_protobuf);
  }
  isbreak = false;
  // 当连接关闭时，还在发数据，会导致sigpipe信号
  sigpipe_action.sa_handler = HandleSigpipe;
  sigemptyset(&sigpipe_action.sa_mask);
  sigpipe_action.sa_flags = 0;
  sigaction(SIGPIPE, &sigpipe_action, NULL);

  timeout = 10;
  timer = new base::Timer(1);
  timer->init(RpcChannelImp::check_call_timeout, this, 1);
  now = time(NULL);
}

RpcChannelImp::~RpcChannelImp() {
  if (!stop) {
    stop = true;
    shutdown(connector->get_connector_fd(), 2);
    connector->close();
    recv_thread->join();
    send_thread->join();
  }
  if (timer != NULL) {
    delete timer;
    timer = NULL;
  }
  if (recv_thread != NULL) {
    delete recv_thread;
  }

  if (send_thread != NULL) {
    delete send_thread;
  }
  if (connector != NULL) {
    delete connector;
    connector = NULL;
  }
  for (auto& session : ticketManager) {
    if (session.second != NULL) {
      delete session.second;
      session.second = NULL;
    }
  }
  if (request_list != NULL) {
    delete request_list;
    request_list = NULL;
  }
}

void RpcChannelImp::CallMethod(const MethodDescriptor* method,
                               RpcController *controller,
                               const Message *request,
                               Message *response,
                               Closure *done) {
  if (done != NULL) {  // 异步
    this->async_all(method, controller, request, response, done);
  } else {  // 同步
    if (this->sync_call(method, controller, request, response) != 0) {
      printf("sync call error\n");
    }
  }
}

sails::ResponsePacket* RpcChannelImp::parser(
    net::Connector *connector) {

  if (connector->readable() < sizeof(int)) {
    return NULL;
  }
  const char* buffer = connector->peek();
  int packetLen = *(reinterpret_cast<const int*>(buffer));
  if (connector->readable() < packetLen + sizeof(int)) {
    return NULL;
  }
  // printf("parse packet len:%d\n", packetLen);

  ResponsePacket* response = new ResponsePacket();
  if (response->ParseFromArray(buffer+sizeof(int), packetLen)) {
    connector->retrieve(packetLen + sizeof(int));
    return response;
  } else {
    // 出错
    delete response;
    connector->retrieve(connector->readable());
  }

  return NULL;
}

void RpcChannelImp::async_all(const google::protobuf::MethodDescriptor *method,
                 google::protobuf::RpcController* ,
                 const google::protobuf::Message* request,
                 google::protobuf::Message* response,
               google::protobuf::Closure* done) {
  if (stop || isbreak) {
    done->Run();
    return;
  }
  std::unique_lock<std::mutex> locker(request_mutex);
  sn++;
  RequestPacket *packet = new RequestPacket();
  packet->set_version(VERSION_MAJOR*1000+VERSION_MINOR*100+VERSION_PATCH);
  packet->set_type(MessageType::RPC_REQUEST);
  packet->set_sn(sn);
  packet->set_servicename(method->service()->name());
  packet->set_funcname(method->name());
  packet->mutable_detail()->PackFrom(*request);
  packet->set_timeout(timeout);

  ticketManager[sn] = new TicketSession(sn, response, done);
  ticketManager[sn]->calltime = now;
  if (!request_list->push_back(packet)) {
    perror("async call too fast");
    delete ticketManager[sn];
    ticketManager[sn] = NULL;
  }
}

int RpcChannelImp::sync_call(const google::protobuf::MethodDescriptor *method,
                             google::protobuf::RpcController *,
                             const google::protobuf::Message *request,
                             google::protobuf::Message *response) {
  if (stop || isbreak) {
    return -1;
  }
  std::unique_lock<std::mutex> locker(request_mutex);
  sn++;
  RequestPacket *packet = new RequestPacket();
  packet->set_version(VERSION_MAJOR*1000+VERSION_MINOR*100+VERSION_PATCH);
  packet->set_type(MessageType::RPC_REQUEST);
  packet->set_sn(sn);
  packet->set_servicename(method->service()->name());
  packet->set_funcname(method->name());
  packet->mutable_detail()->PackFrom(*request);
  packet->set_timeout(timeout);

  ticketManager[sn] = new TicketSession(sn, response, NULL);
  ticketManager[sn]->calltime = now;
  request_list->push_back(packet);

  // wait notify
  while (ticketManager[sn]->errcode == 1) {
    ticketManager[sn]->notify.wait(locker);
  }
  int errorcode = ticketManager[sn]->errcode;
  delete ticketManager[sn];
  ticketManager.erase(sn);
  return errorcode;
}

void RpcChannelImp::send_request(RpcChannelImp* channel) {
  while (!channel->stop) {
    RequestPacket *request = NULL;
    static int empty_request = 0;
    channel->request_list->pop_front(request, 100);
    if (request != NULL) {
      empty_request = 0;
      std::string data = request->SerializeAsString();
      int len = data.length();
      channel->connector->write(reinterpret_cast<char*>(&len), sizeof(int));
      channel->connector->write(data.c_str(), data.length());
      if (channel->connector->send() > 0) {
        delete request;
      } else {
        // 0表示连接关闭，小于0表示出错
        // 这里不主动设置stop的标识，由recv_response来设置，这样可以由它来
        // 处理后续回调和通知
        channel->reset_ticket();
        delete request;
      }
    } else {
      empty_request++;
      // 超过50次，也就是5s没有发送过数据，则发送一个心跳包
      if (empty_request > 50) {
        empty_request = 0;
        if (!channel->keeplive) {
          continue;
        }
        RequestPacket heartbeat;
        heartbeat.set_version(
            VERSION_MAJOR*1000+VERSION_MINOR*100+VERSION_PATCH);
        heartbeat.set_type(MessageType::PING);
        heartbeat.set_sn(0);
        heartbeat.set_servicename("");
        heartbeat.set_funcname("");
        std::string data = heartbeat.SerializeAsString();
        int len = data.length();
        channel->connector->write(reinterpret_cast<char*>(&len), sizeof(int));
        channel->connector->write(data.c_str(), data.length());
        if (channel->connector->send() > 0) {
          // 成功
        } else {
          // 失败
        }
      }
    }
  }
}

void RpcChannelImp::recv_response(RpcChannelImp* channel) {
  while (!channel->stop) {
    // 为了结束时防止阻塞到这里不能返回，通过调用close使它返回
    int n = channel->connector->read();
    if (n > 0) {
      bool continueParse = false;
      do {
        continueParse = false;
        if (channel->connector->readable() == 0) {
          break;  // 解析一次完成
        }
        ResponsePacket *resp = RpcChannelImp::parser(channel->connector);
        if (resp != NULL) {
          if (resp->ret() == ErrorCode::ERR_SUCCESS) {
            if (resp->type() == MessageType::PING) {
              delete(resp);
              continue;
            }
            std::unique_lock<std::mutex> locker(channel->request_mutex);
            if (channel->ticketManager[resp->sn()] != NULL) {
              resp->detail().UnpackTo(
                  channel->ticketManager[resp->sn()]->response);
              channel->ticketManager[resp->sn()]->errcode = 0;
              if (channel->ticketManager[resp->sn()]->done != NULL) {
                // 异步，那么就在这里调用
                channel->ticketManager[resp->sn()]->done->Run();
                delete channel->ticketManager[resp->sn()];
                channel->ticketManager.erase(resp->sn());
              } else {
                // 同步，通知调用线程
                channel->ticketManager[resp->sn()]->notify.notify_all();
              }
              continueParse = true;
            }
          } else {
            char msg[50];
            snprintf(msg, sizeof(msg), "get a response for error_code %d",
                     resp->ret());
            perror(msg);
          }
          delete(resp);
        } else {
          // 接收的数据不够，等待再次接收后再解析
          continueParse = false;
        }
      } while (continueParse);
    } else {  // 0表示连接被关闭, 小于0，出错
      // perror("recv");
      // printf("read N:%d\n", n);
      // 由于response是一个自定义的结构
      // 所以没有errcode之类的标识，这里就只能由用户自己通过response没有
      // 设置来得知是出错了
      if (n == -1 && errno == EAGAIN) {  // 可能被中断了,重新接收
      } else {  // 出错
        channel->isbreak = true;
        channel->reset_ticket();
        if (channel->keeplive && !channel->stop) {  // 连接关闭了
          // 重连
          while (!channel->stop) {
            sleep(2);
            channel->connector->close();
            if (channel->connector->connect(
                    channel->ip.c_str(), channel->port, true)) {
              channel->isbreak = false;
              break;
            } else {
            }
          }
        }
      }
    }
  }
}

void RpcChannelImp::reset_ticket() {
  std::unique_lock<std::mutex> locker(request_mutex);
  for (auto& ticket : ticketManager) {
    if (ticket.second != NULL) {
      ticket.second->errcode = -1;
      if (ticket.second->done == NULL) {  // 是同步，通知主线程返回
        ticketManager[ticket.first]->notify.notify_all();
      } else {  // 异步
        ticket.second->done->Run();
        delete ticket.second;
        ticket.second = NULL;
      }
    }
  }
}

void RpcChannelImp::check_call_timeout(void * data) {
  if (data != NULL) {
    RpcChannelImp* channel = reinterpret_cast<RpcChannelImp*>(data);
    if (channel->timeout <= 0) {
      return;
    }
    channel->now = time(NULL);
    std::unique_lock<std::mutex> locker(channel->request_mutex);
    for (auto iter = channel->ticketManager.begin();
         iter != channel->ticketManager.end(); ) {
      // 超时
      TicketSession* session = iter->second;
      if (session != NULL
          && channel->now - session->calltime > channel->timeout) {
        session->errcode = -3;
        if (session->done == NULL) {  // 是同步，通知主线程返回
          session->notify.notify_all();
          ++iter;
        } else {  // 异步
          session->done->Run();
          delete session;
          iter->second = NULL;
          iter = channel->ticketManager.erase(iter);
        }
      } else {
        ++iter;
      }
    }
  }
}

}  // namespace sails
<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright © 2017-2018 Ruben Van Boxem
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/

#include "test.h++"

#include <core/event_loop.h++>

#include <thread>
#include <iostream>

namespace
{
  using skui::test::check;

  struct fixture
  {
    int count = 0;
    void f() { ++count; }

    void test_execute_stop()
    {
      skui::core::event_loop event_loop;
      int exit_code {0};

      std::thread thread([&event_loop, &exit_code] { exit_code = event_loop.execute(); });

      event_loop.push(std::make_unique<skui::core::command>([this] { f(); }));
      event_loop.push(std::make_unique<skui::core::command>([this] { f(); }));
      event_loop.push(std::make_unique<skui::core::command>([this] { f(); }));
      event_loop.stop(1);

      thread.join();

      check(count == 3, "3 commands executed.");
      check(exit_code == 1, "proper exit code returned");
    }

    void test_execute_interrupt()
    {
      skui::core::event_loop event_loop;
      int exit_code {0};

      std::thread thread([&event_loop, &exit_code] { exit_code = event_loop.execute(); });

      std::mutex mutex;
      std::unique_lock lock{mutex};
      std::condition_variable condition;
      std::atomic<bool> ready = false;
      event_loop.push(std::make_unique<skui::core::command>([&condition, &lock, &ready]
                                                            { condition.wait(lock, [&ready] { return ready.load(); }); }));
      event_loop.push(std::make_unique<skui::core::command>([this] { f(); }));
      event_loop.interrupt(1);
      ready = true;
      condition.notify_one();

      thread.join();

      check(count == 0, "Interrupt prevents further command execution.");
      check(exit_code == 1, "Interrupt returns proper exit code.");
    }

    void test_construct_execute_stop()
    {
      skui::core::command_queue::commands_type commands;
      commands.push_back(std::make_unique<skui::core::command>([this] { f(); }));
      skui::core::event_loop event_loop{std::move(commands)};
      int exit_code {0};

      std::thread thread([&event_loop, &exit_code] { exit_code = event_loop.execute(); });
      event_loop.stop(1);

      thread.join();

      check(count == 1, "1 commands executed.");
      check(exit_code == 1, "proper exit code returned");
    }

    void test_filter()
    {
      std::size_t number_of_commands = 0;
      auto filter = [&number_of_commands](skui::core::command_queue::commands_type& commands) { number_of_commands = commands.size(); };
      skui::core::command_queue::commands_type commands;
      commands.push_back(std::make_unique<skui::core::command>([this] { f(); }));

      skui::core::event_loop event_loop{filter, std::move(commands)};

      event_loop.stop(); // puts event in queue to stop
      event_loop.execute();

      check(number_of_commands == 2, "filter called on commands in event_loop");
    }
  };
}

int main()
{
  fixture{}.test_execute_stop();
  fixture{}.test_execute_interrupt();
  fixture{}.test_construct_execute_stop();
  fixture{}.test_filter();

  return skui::test::exit_code;
}
<commit_msg>Attempt to fix event_loop::interrupt test.<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright © 2017-2018 Ruben Van Boxem
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/

#include "test.h++"

#include <core/event_loop.h++>

#include <thread>
#include <iostream>

namespace
{
  using skui::test::check;

  struct fixture
  {
    int count = 0;
    void f() { ++count; }

    void test_execute_stop()
    {
      skui::core::event_loop event_loop;
      int exit_code {0};

      std::thread thread([&event_loop, &exit_code] { exit_code = event_loop.execute(); });

      event_loop.push(std::make_unique<skui::core::command>([this] { f(); }));
      event_loop.push(std::make_unique<skui::core::command>([this] { f(); }));
      event_loop.push(std::make_unique<skui::core::command>([this] { f(); }));
      event_loop.stop(1);

      thread.join();

      check(count == 3, "3 commands executed.");
      check(exit_code == 1, "proper exit code returned");
    }

    // This is only a baseline check, it cannot verify all scheduling possibilities
    //thread 1                     | thread 2
    // start                       |
    //                             | start
    // lock mutex                  |
    // ready = false               |
    // *add command1*              |
    // unlock mutex/wait condition |
    //                             | [command1] lock mutex
    //                             | [command1] ready = true
    //                             | [command1] unlock mutex
    //                             | [command1] notify condition
    // [condition] locks mutex     |
    // *add command 2*             |
    // *add interrupt command*     |
    // unlock mutex2               |
    //                             | [command1] lock mutex2
    //                             | [command1] finish
    //                             | [interrupt command] exit = false before command2
    //                           join
    // check 2 commands executed
    void test_execute_interrupt()
    {
      skui::core::event_loop event_loop;
      int exit_code {0};

      std::thread thread([&event_loop, &exit_code] { exit_code = event_loop.execute(); });

      std::mutex mutex, mutex2;
      std::condition_variable condition;
      std::atomic_bool ready;

      std::unique_lock lock{mutex}; // block command until we wait on condition
      std::unique_lock lock2{mutex2}; // block part 2 of command
      event_loop.push(std::make_unique<skui::core::command>([&mutex, &mutex2,
                                                             &condition,
                                                             &ready]
                                                            {
                                                              // block command until wait on condition has been called
                                                              std::unique_lock thread_lock{mutex};
                                                              ready = true;
                                                              thread_lock.unlock();
                                                              condition.notify_one();

                                                              // block command until main thread has requested interrupt
                                                              std::unique_lock thread_lock2{mutex2};
                                                            }));
      condition.wait(lock,
                     [&ready] { return ready.load(); });

      event_loop.push(std::make_unique<skui::core::command>([this] { f(); }));
      event_loop.interrupt(1);

      lock2.unlock();
      thread.join(); // wait for event_loop to exit

      check(count == 0, "Interrupt prevents further command execution.");
      check(exit_code == 1, "Interrupt returns proper exit code.");
    }

    void test_construct_execute_stop()
    {
      skui::core::command_queue::commands_type commands;
      commands.push_back(std::make_unique<skui::core::command>([this] { f(); }));
      skui::core::event_loop event_loop{std::move(commands)};
      int exit_code {0};

      std::thread thread([&event_loop, &exit_code] { exit_code = event_loop.execute(); });
      event_loop.stop(1);

      thread.join();

      check(count == 1, "1 commands executed.");
      check(exit_code == 1, "proper exit code returned");
    }

    void test_filter()
    {
      std::size_t number_of_commands = 0;
      auto filter = [&number_of_commands](skui::core::command_queue::commands_type& commands) { number_of_commands = commands.size(); };
      skui::core::command_queue::commands_type commands;
      commands.push_back(std::make_unique<skui::core::command>([this] { f(); }));

      skui::core::event_loop event_loop{filter, std::move(commands)};

      event_loop.stop(); // puts event in queue to stop
      event_loop.execute();

      check(number_of_commands == 2, "filter called on commands in event_loop");
    }
  };
}

int main()
{
  fixture{}.test_execute_stop();
  fixture{}.test_execute_interrupt();
  fixture{}.test_construct_execute_stop();
  fixture{}.test_filter();

  return skui::test::exit_code;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#ifndef MFEM_NONLININTEG
#define MFEM_NONLININTEG

#include "../config/config.hpp"
#include "fe.hpp"
#include "coefficient.hpp"
#include "fespace.hpp"

namespace mfem
{

/** @brief This class is used to express the local action of a general nonlinear
    finite element operator. In addition it may provide the capability to
    assemble the local gradient operator and to compute the local energy. */
class NonlinearFormIntegrator
{
protected:
   const IntegrationRule *IntRule;

   NonlinearFormIntegrator(const IntegrationRule *ir = NULL)
      : IntRule(ir) { }

public:
   /** @brief Prescribe a fixed IntegrationRule to use (when @a ir != NULL) or
       let the integrator choose (when @a ir == NULL). */
   void SetIntRule(const IntegrationRule *ir)
   {
      IntRule = ir;
   }

   /// Prescribe a fixed IntegrationRule to use.
   void SetIntegrationRule(const IntegrationRule &irule)
   {
      IntRule = &irule;
   }

   /// Perform the local action of the NonlinearFormIntegrator
   virtual void AssembleElementVector(const FiniteElement &el,
                                      ElementTransformation &Tr,
                                      const Vector &elfun, Vector &elvect);

   /// @brief Perform the local action of the NonlinearFormIntegrator resulting
   /// from a face integral term.
   virtual void AssembleFaceVector(const FiniteElement &el1,
                                   const FiniteElement &el2,
                                   FaceElementTransformations &Tr,
                                   const Vector &elfun, Vector &elvect);

   /// Assemble the local gradient matrix
   virtual void AssembleElementGrad(const FiniteElement &el,
                                    ElementTransformation &Tr,
                                    const Vector &elfun, DenseMatrix &elmat);

   /// @brief Assemble the local action of the gradient of the
   /// NonlinearFormIntegrator resulting from a face integral term.
   virtual void AssembleFaceGrad(const FiniteElement &el1,
                                 const FiniteElement &el2,
                                 FaceElementTransformations &Tr,
                                 const Vector &elfun, DenseMatrix &elmat);

   /// Compute the local energy
   virtual double GetElementEnergy(const FiniteElement &el,
                                   ElementTransformation &Tr,
                                   const Vector &elfun);

   /// Method defining partial assembly.
   /** The result of the partial assembly is stored internally so that it can be
       used later in the methods AddMultPA(). */
   virtual void AssemblePA(const FiniteElementSpace &fes);

   /** The result of the partial assembly is stored internally so that it can be
       used later in the methods AddMultPA().
       Used with BilinearFormIntegrators that have different spaces. */
   virtual void AssemblePA(const FiniteElementSpace &trial_fes,
                           const FiniteElementSpace &test_fes);

   /// Method for partially assembled action.
   /** Perform the action of integrator on the input @a x and add the result to
       the output @a y. Both @a x and @a y are E-vectors, i.e. they represent
       the element-wise discontinuous version of the FE space.

       This method can be called only after the method AssemblePA() has been
       called. */
   virtual void AddMultPA(const Vector &x, Vector &y) const;

   virtual ~NonlinearFormIntegrator() { }
};

/** The abstract base class BlockNonlinearFormIntegrator is
    a generalization of the NonlinearFormIntegrator class suitable
    for block state vectors. */
class BlockNonlinearFormIntegrator
{
public:
   /// Compute the local energy
   virtual double GetElementEnergy(const Array<const FiniteElement *>&el,
                                   ElementTransformation &Tr,
                                   const Array<const Vector *>&elfun);

   /// Perform the local action of the BlockNonlinearFormIntegrator
   virtual void AssembleElementVector(const Array<const FiniteElement *> &el,
                                      ElementTransformation &Tr,
                                      const Array<const Vector *> &elfun,
                                      const Array<Vector *> &elvec);

   virtual void AssembleFaceVector(const Array<const FiniteElement *> &el1,
                                   const Array<const FiniteElement *> &el2,
                                   FaceElementTransformations &Tr,
                                   const Array<const Vector *> &elfun,
                                   const Array<Vector *> &elvect);

   /// Assemble the local gradient matrix
   virtual void AssembleElementGrad(const Array<const FiniteElement*> &el,
                                    ElementTransformation &Tr,
                                    const Array<const Vector *> &elfun,
                                    const Array2D<DenseMatrix *> &elmats);

   virtual void AssembleFaceGrad(const Array<const FiniteElement *>&el1,
                                 const Array<const FiniteElement *>&el2,
                                 FaceElementTransformations &Tr,
                                 const Array<const Vector *> &elfun,
                                 const Array2D<DenseMatrix *> &elmats);

   virtual ~BlockNonlinearFormIntegrator() { }
};

/// Abstract class for hyperelastic models
class HyperelasticModel
{
protected:
   ElementTransformation *Ttr; /**< Reference-element to target-element
                                    transformation. */

public:
   HyperelasticModel() : Ttr(NULL) { }
   virtual ~HyperelasticModel() { }

   /// A reference-element to target-element transformation that can be used to
   /// evaluate Coefficient%s.
   /** @note It is assumed that _Ttr.SetIntPoint() is already called for the
       point of interest. */
   void SetTransformation(ElementTransformation &_Ttr)
   {
      Ttr = &_Ttr;
   }

   /** @brief Evaluate the strain energy density function, W = W(Jpt).
       @param[in] Jpt  Represents the target->physical transformation
                       Jacobian matrix. */
   virtual double EvalW(const DenseMatrix &Jpt) const = 0;

   /** @brief Evaluate the 1st Piola-Kirchhoff stress tensor, P = P(Jpt).
       @param[in] Jpt  Represents the target->physical transformation
                       Jacobian matrix.
       @param[out]  P  The evaluated 1st Piola-Kirchhoff stress tensor. */
   virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const = 0;

   /** @brief Evaluate the derivative of the 1st Piola-Kirchhoff stress tensor
       and assemble its contribution to the local gradient matrix 'A'.
       @param[in] Jpt     Represents the target->physical transformation
                          Jacobian matrix.
       @param[in] DS      Gradient of the basis matrix (dof x dim).
       @param[in] weight  Quadrature weight coefficient for the point.
       @param[in,out]  A  Local gradient matrix where the contribution from this
                          point will be added.

       Computes weight * d(dW_dxi)_d(xj) at the current point, for all i and j,
       where x1 ... xn are the FE dofs. This function is usually defined using
       the matrix invariants and their derivatives.
   */
   virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
                          const double weight, DenseMatrix &A) const = 0;
};

/** Inverse-harmonic hyperelastic model with a strain energy density function
    given by the formula: W(J) = (1/2) det(J) Tr((J J^t)^{-1}) where J is the
    deformation gradient. */
class InverseHarmonicModel : public HyperelasticModel
{
protected:
   mutable DenseMatrix Z, S; // dim x dim
   mutable DenseMatrix G, C; // dof x dim

public:
   virtual double EvalW(const DenseMatrix &J) const;

   virtual void EvalP(const DenseMatrix &J, DenseMatrix &P) const;

   virtual void AssembleH(const DenseMatrix &J, const DenseMatrix &DS,
                          const double weight, DenseMatrix &A) const;
};

/** Neo-Hookean hyperelastic model with a strain energy density function given
    by the formula: \f$(\mu/2)(\bar{I}_1 - dim) + (K/2)(det(J)/g - 1)^2\f$ where
    J is the deformation gradient and \f$\bar{I}_1 = (det(J))^{-2/dim} Tr(J
    J^t)\f$. The parameters \f$\mu\f$ and K are the shear and bulk moduli,
    respectively, and g is a reference volumetric scaling. */
class NeoHookeanModel : public HyperelasticModel
{
protected:
   mutable double mu, K, g;
   Coefficient *c_mu, *c_K, *c_g;
   bool have_coeffs;

   mutable DenseMatrix Z;    // dim x dim
   mutable DenseMatrix G, C; // dof x dim

   inline void EvalCoeffs() const;

public:
   NeoHookeanModel(double _mu, double _K, double _g = 1.0)
      : mu(_mu), K(_K), g(_g), have_coeffs(false)
   {
      c_mu = c_K = c_g = NULL;
   }

   NeoHookeanModel(Coefficient &_mu, Coefficient &_K, Coefficient *_g = NULL)
      : mu(0.0), K(0.0), g(1.0), c_mu(&_mu), c_K(&_K), c_g(_g),
        have_coeffs(true) { }

   virtual double EvalW(const DenseMatrix &J) const;

   virtual void EvalP(const DenseMatrix &J, DenseMatrix &P) const;

   virtual void AssembleH(const DenseMatrix &J, const DenseMatrix &DS,
                          const double weight, DenseMatrix &A) const;
};

/** Hyperelastic integrator for any given HyperelasticModel.

    Represents @f$ \int W(Jpt) dx @f$ over a target zone, where W is the
    @a model's strain energy density function, and Jpt is the Jacobian of the
    target->physical coordinates transformation. The target configuration is
    given by the current mesh at the time of the evaluation of the integrator.
*/
class HyperelasticNLFIntegrator : public NonlinearFormIntegrator
{
private:
   HyperelasticModel *model;

   //   Jrt: the Jacobian of the target-to-reference-element transformation.
   //   Jpr: the Jacobian of the reference-to-physical-element transformation.
   //   Jpt: the Jacobian of the target-to-physical-element transformation.
   //     P: represents dW_d(Jtp) (dim x dim).
   //   DSh: gradients of reference shape functions (dof x dim).
   //    DS: gradients of the shape functions in the target (stress-free)
   //        configuration (dof x dim).
   // PMatI: coordinates of the deformed configuration (dof x dim).
   // PMatO: reshaped view into the local element contribution to the operator
   //        output - the result of AssembleElementVector() (dof x dim).
   DenseMatrix DSh, DS, Jrt, Jpr, Jpt, P, PMatI, PMatO;

public:
   /** @param[in] m  HyperelasticModel that will be integrated. */
   HyperelasticNLFIntegrator(HyperelasticModel *m) : model(m) { }

   /** @brief Computes the integral of W(Jacobian(Trt)) over a target zone
       @param[in] el     Type of FiniteElement.
       @param[in] Ttr    Represents ref->target coordinates transformation.
       @param[in] elfun  Physical coordinates of the zone. */
   virtual double GetElementEnergy(const FiniteElement &el,
                                   ElementTransformation &Ttr,
                                   const Vector &elfun);

   virtual void AssembleElementVector(const FiniteElement &el,
                                      ElementTransformation &Ttr,
                                      const Vector &elfun, Vector &elvect);

   virtual void AssembleElementGrad(const FiniteElement &el,
                                    ElementTransformation &Ttr,
                                    const Vector &elfun, DenseMatrix &elmat);
};

/** Hyperelastic incompressible Neo-Hookean integrator with the PK1 stress
    \f$P = \mu F - p F^{-T}\f$ where \f$\mu\f$ is the shear modulus,
    \f$p\f$ is the pressure, and \f$F\f$ is the deformation gradient */
class IncompressibleNeoHookeanIntegrator : public BlockNonlinearFormIntegrator
{
private:
   Coefficient *c_mu;
   DenseMatrix DSh_u, DS_u, J0i, J, J1, Finv, P, F, FinvT;
   DenseMatrix PMatI_u, PMatO_u, PMatI_p, PMatO_p, Z, G, C;
   Vector Sh_p;

public:
   IncompressibleNeoHookeanIntegrator(Coefficient &_mu) : c_mu(&_mu) { }

   virtual double GetElementEnergy(const Array<const FiniteElement *>&el,
                                   ElementTransformation &Tr,
                                   const Array<const Vector *> &elfun);

   /// Perform the local action of the NonlinearFormIntegrator
   virtual void AssembleElementVector(const Array<const FiniteElement *> &el,
                                      ElementTransformation &Tr,
                                      const Array<const Vector *> &elfun,
                                      const Array<Vector *> &elvec);

   /// Assemble the local gradient matrix
   virtual void AssembleElementGrad(const Array<const FiniteElement*> &el,
                                    ElementTransformation &Tr,
                                    const Array<const Vector *> &elfun,
                                    const Array2D<DenseMatrix *> &elmats);
};

class VectorConvectionNLFIntegrator : public NonlinearFormIntegrator
{
private:
   Coefficient *Q{};
   DenseMatrix dshape, dshapex, EF, gradEF, ELV, elmat_comp;
   Vector shape;
   // PA extension
   Vector pa_data;
   const DofToQuad *maps;         ///< Not owned
   const GeometricFactors *geom;  ///< Not owned
   int dim, ne, nq;
public:
   VectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { }

   VectorConvectionNLFIntegrator() = default;

   static const IntegrationRule &GetRule(const FiniteElement &fe,
                                         ElementTransformation &T);

   virtual void AssembleElementVector(const FiniteElement &el,
                                      ElementTransformation &trans,
                                      const Vector &elfun,
                                      Vector &elvect);

   virtual void AssembleElementGrad(const FiniteElement &el,
                                    ElementTransformation &trans,
                                    const Vector &elfun,
                                    DenseMatrix &elmat);

   using NonlinearFormIntegrator::AssemblePA;

   virtual void AssemblePA(const FiniteElementSpace &fes);

   virtual void AddMultPA(const Vector &x, Vector &y) const;
};

/** @brief This class is used to assemble the skew-symmetric form of the
nonlinear  term arising in the Navier Stokes equations
\f$(u \cdot \nabla v, w )\f$*/
class ConvectiveVectorConvectionNLFIntegrator : public
   VectorConvectionNLFIntegrator
{
private:
   Coefficient *Q{};
   DenseMatrix dshape, dshapex, EF, gradEF, ELV, elmat_comp;
   Vector shape;
public:
   ConvectiveVectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { }

   ConvectiveVectorConvectionNLFIntegrator() = default;

   virtual void AssembleElementGrad(const FiniteElement &el,
                                    ElementTransformation &trans,
                                    const Vector &elfun,
                                    DenseMatrix &elmat);

};
/** @brief This class is used to assemble the skew-symmetric form of the nonlinear
term arising in the Navier Stokes equations \f$.5*(u \cdot \nabla v, w ) -
.5*(u \cdot \nabla w, v )\f$ */
class SkewSymmetricVectorConvectionNLFIntegrator : public
   VectorConvectionNLFIntegrator
{
private:
   Coefficient *Q{};
   DenseMatrix dshape, dshapex, EF, gradEF, ELV, elmat_comp;
   Vector shape;
public:
   SkewSymmetricVectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { }

   SkewSymmetricVectorConvectionNLFIntegrator() = default;

   virtual void AssembleElementGrad(const FiniteElement &el,
                                    ElementTransformation &trans,
                                    const Vector &elfun,
                                    DenseMatrix &elmat);

};

}

#endif
<commit_msg>fixed formatting and comments<commit_after>// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#ifndef MFEM_NONLININTEG
#define MFEM_NONLININTEG

#include "../config/config.hpp"
#include "fe.hpp"
#include "coefficient.hpp"
#include "fespace.hpp"

namespace mfem
{

/** @brief This class is used to express the local action of a general nonlinear
    finite element operator. In addition it may provide the capability to
    assemble the local gradient operator and to compute the local energy. */
class NonlinearFormIntegrator
{
protected:
   const IntegrationRule *IntRule;

   NonlinearFormIntegrator(const IntegrationRule *ir = NULL)
      : IntRule(ir) { }

public:
   /** @brief Prescribe a fixed IntegrationRule to use (when @a ir != NULL) or
       let the integrator choose (when @a ir == NULL). */
   void SetIntRule(const IntegrationRule *ir) { IntRule = ir; }

   /// Prescribe a fixed IntegrationRule to use.
   void SetIntegrationRule(const IntegrationRule &irule) { IntRule = &irule; }

   /// Perform the local action of the NonlinearFormIntegrator
   virtual void AssembleElementVector(const FiniteElement &el,
                                      ElementTransformation &Tr,
                                      const Vector &elfun, Vector &elvect);

   /// @brief Perform the local action of the NonlinearFormIntegrator resulting
   /// from a face integral term.
   virtual void AssembleFaceVector(const FiniteElement &el1,
                                   const FiniteElement &el2,
                                   FaceElementTransformations &Tr,
                                   const Vector &elfun, Vector &elvect);

   /// Assemble the local gradient matrix
   virtual void AssembleElementGrad(const FiniteElement &el,
                                    ElementTransformation &Tr,
                                    const Vector &elfun, DenseMatrix &elmat);

   /// @brief Assemble the local action of the gradient of the
   /// NonlinearFormIntegrator resulting from a face integral term.
   virtual void AssembleFaceGrad(const FiniteElement &el1,
                                 const FiniteElement &el2,
                                 FaceElementTransformations &Tr,
                                 const Vector &elfun, DenseMatrix &elmat);

   /// Compute the local energy
   virtual double GetElementEnergy(const FiniteElement &el,
                                   ElementTransformation &Tr,
                                   const Vector &elfun);

   /// Method defining partial assembly.
   /** The result of the partial assembly is stored internally so that it can be
       used later in the methods AddMultPA(). */
   virtual void AssemblePA(const FiniteElementSpace &fes);

   /** The result of the partial assembly is stored internally so that it can be
       used later in the methods AddMultPA().
       Used with BilinearFormIntegrators that have different spaces. */
   virtual void AssemblePA(const FiniteElementSpace &trial_fes,
                           const FiniteElementSpace &test_fes);

   /// Method for partially assembled action.
   /** Perform the action of integrator on the input @a x and add the result to
       the output @a y. Both @a x and @a y are E-vectors, i.e. they represent
       the element-wise discontinuous version of the FE space.

       This method can be called only after the method AssemblePA() has been
       called. */
   virtual void AddMultPA(const Vector &x, Vector &y) const;

   virtual ~NonlinearFormIntegrator() { }
};

/** The abstract base class BlockNonlinearFormIntegrator is
    a generalization of the NonlinearFormIntegrator class suitable
    for block state vectors. */
class BlockNonlinearFormIntegrator
{
public:
   /// Compute the local energy
   virtual double GetElementEnergy(const Array<const FiniteElement *>&el,
                                   ElementTransformation &Tr,
                                   const Array<const Vector *>&elfun);

   /// Perform the local action of the BlockNonlinearFormIntegrator
   virtual void AssembleElementVector(const Array<const FiniteElement *> &el,
                                      ElementTransformation &Tr,
                                      const Array<const Vector *> &elfun,
                                      const Array<Vector *> &elvec);

   virtual void AssembleFaceVector(const Array<const FiniteElement *> &el1,
                                   const Array<const FiniteElement *> &el2,
                                   FaceElementTransformations &Tr,
                                   const Array<const Vector *> &elfun,
                                   const Array<Vector *> &elvect);

   /// Assemble the local gradient matrix
   virtual void AssembleElementGrad(const Array<const FiniteElement*> &el,
                                    ElementTransformation &Tr,
                                    const Array<const Vector *> &elfun,
                                    const Array2D<DenseMatrix *> &elmats);

   virtual void AssembleFaceGrad(const Array<const FiniteElement *>&el1,
                                 const Array<const FiniteElement *>&el2,
                                 FaceElementTransformations &Tr,
                                 const Array<const Vector *> &elfun,
                                 const Array2D<DenseMatrix *> &elmats);

   virtual ~BlockNonlinearFormIntegrator() { }
};

/// Abstract class for hyperelastic models
class HyperelasticModel
{
protected:
   ElementTransformation *Ttr; /**< Reference-element to target-element
                                    transformation. */

public:
   HyperelasticModel() : Ttr(NULL) { }
   virtual ~HyperelasticModel() { }

   /// A reference-element to target-element transformation that can be used to
   /// evaluate Coefficient%s.
   /** @note It is assumed that _Ttr.SetIntPoint() is already called for the
       point of interest. */
   void SetTransformation(ElementTransformation &_Ttr) { Ttr = &_Ttr; }

   /** @brief Evaluate the strain energy density function, W = W(Jpt).
       @param[in] Jpt  Represents the target->physical transformation
                       Jacobian matrix. */
   virtual double EvalW(const DenseMatrix &Jpt) const = 0;

   /** @brief Evaluate the 1st Piola-Kirchhoff stress tensor, P = P(Jpt).
       @param[in] Jpt  Represents the target->physical transformation
                       Jacobian matrix.
       @param[out]  P  The evaluated 1st Piola-Kirchhoff stress tensor. */
   virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const = 0;

   /** @brief Evaluate the derivative of the 1st Piola-Kirchhoff stress tensor
       and assemble its contribution to the local gradient matrix 'A'.
       @param[in] Jpt     Represents the target->physical transformation
                          Jacobian matrix.
       @param[in] DS      Gradient of the basis matrix (dof x dim).
       @param[in] weight  Quadrature weight coefficient for the point.
       @param[in,out]  A  Local gradient matrix where the contribution from this
                          point will be added.

       Computes weight * d(dW_dxi)_d(xj) at the current point, for all i and j,
       where x1 ... xn are the FE dofs. This function is usually defined using
       the matrix invariants and their derivatives.
   */
   virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
                          const double weight, DenseMatrix &A) const = 0;
};

/** Inverse-harmonic hyperelastic model with a strain energy density function
    given by the formula: W(J) = (1/2) det(J) Tr((J J^t)^{-1}) where J is the
    deformation gradient. */
class InverseHarmonicModel : public HyperelasticModel
{
protected:
   mutable DenseMatrix Z, S; // dim x dim
   mutable DenseMatrix G, C; // dof x dim

public:
   virtual double EvalW(const DenseMatrix &J) const;

   virtual void EvalP(const DenseMatrix &J, DenseMatrix &P) const;

   virtual void AssembleH(const DenseMatrix &J, const DenseMatrix &DS,
                          const double weight, DenseMatrix &A) const;
};

/** Neo-Hookean hyperelastic model with a strain energy density function given
    by the formula: \f$(\mu/2)(\bar{I}_1 - dim) + (K/2)(det(J)/g - 1)^2\f$ where
    J is the deformation gradient and \f$\bar{I}_1 = (det(J))^{-2/dim} Tr(J
    J^t)\f$. The parameters \f$\mu\f$ and K are the shear and bulk moduli,
    respectively, and g is a reference volumetric scaling. */
class NeoHookeanModel : public HyperelasticModel
{
protected:
   mutable double mu, K, g;
   Coefficient *c_mu, *c_K, *c_g;
   bool have_coeffs;

   mutable DenseMatrix Z;    // dim x dim
   mutable DenseMatrix G, C; // dof x dim

   inline void EvalCoeffs() const;

public:
   NeoHookeanModel(double _mu, double _K, double _g = 1.0)
      : mu(_mu), K(_K), g(_g), have_coeffs(false) { c_mu = c_K = c_g = NULL; }

   NeoHookeanModel(Coefficient &_mu, Coefficient &_K, Coefficient *_g = NULL)
      : mu(0.0), K(0.0), g(1.0), c_mu(&_mu), c_K(&_K), c_g(_g),
        have_coeffs(true) { }

   virtual double EvalW(const DenseMatrix &J) const;

   virtual void EvalP(const DenseMatrix &J, DenseMatrix &P) const;

   virtual void AssembleH(const DenseMatrix &J, const DenseMatrix &DS,
                          const double weight, DenseMatrix &A) const;
};

/** Hyperelastic integrator for any given HyperelasticModel.

    Represents @f$ \int W(Jpt) dx @f$ over a target zone, where W is the
    @a model's strain energy density function, and Jpt is the Jacobian of the
    target->physical coordinates transformation. The target configuration is
    given by the current mesh at the time of the evaluation of the integrator.
*/
class HyperelasticNLFIntegrator : public NonlinearFormIntegrator
{
private:
   HyperelasticModel *model;

   //   Jrt: the Jacobian of the target-to-reference-element transformation.
   //   Jpr: the Jacobian of the reference-to-physical-element transformation.
   //   Jpt: the Jacobian of the target-to-physical-element transformation.
   //     P: represents dW_d(Jtp) (dim x dim).
   //   DSh: gradients of reference shape functions (dof x dim).
   //    DS: gradients of the shape functions in the target (stress-free)
   //        configuration (dof x dim).
   // PMatI: coordinates of the deformed configuration (dof x dim).
   // PMatO: reshaped view into the local element contribution to the operator
   //        output - the result of AssembleElementVector() (dof x dim).
   DenseMatrix DSh, DS, Jrt, Jpr, Jpt, P, PMatI, PMatO;

public:
   /** @param[in] m  HyperelasticModel that will be integrated. */
   HyperelasticNLFIntegrator(HyperelasticModel *m) : model(m) { }

   /** @brief Computes the integral of W(Jacobian(Trt)) over a target zone
       @param[in] el     Type of FiniteElement.
       @param[in] Ttr    Represents ref->target coordinates transformation.
       @param[in] elfun  Physical coordinates of the zone. */
   virtual double GetElementEnergy(const FiniteElement &el,
                                   ElementTransformation &Ttr,
                                   const Vector &elfun);

   virtual void AssembleElementVector(const FiniteElement &el,
                                      ElementTransformation &Ttr,
                                      const Vector &elfun, Vector &elvect);

   virtual void AssembleElementGrad(const FiniteElement &el,
                                    ElementTransformation &Ttr,
                                    const Vector &elfun, DenseMatrix &elmat);
};

/** Hyperelastic incompressible Neo-Hookean integrator with the PK1 stress
    \f$P = \mu F - p F^{-T}\f$ where \f$\mu\f$ is the shear modulus,
    \f$p\f$ is the pressure, and \f$F\f$ is the deformation gradient */
class IncompressibleNeoHookeanIntegrator : public BlockNonlinearFormIntegrator
{
private:
   Coefficient *c_mu;
   DenseMatrix DSh_u, DS_u, J0i, J, J1, Finv, P, F, FinvT;
   DenseMatrix PMatI_u, PMatO_u, PMatI_p, PMatO_p, Z, G, C;
   Vector Sh_p;

public:
   IncompressibleNeoHookeanIntegrator(Coefficient &_mu) : c_mu(&_mu) { }

   virtual double GetElementEnergy(const Array<const FiniteElement *>&el,
                                   ElementTransformation &Tr,
                                   const Array<const Vector *> &elfun);

   /// Perform the local action of the NonlinearFormIntegrator
   virtual void AssembleElementVector(const Array<const FiniteElement *> &el,
                                      ElementTransformation &Tr,
                                      const Array<const Vector *> &elfun,
                                      const Array<Vector *> &elvec);

   /// Assemble the local gradient matrix
   virtual void AssembleElementGrad(const Array<const FiniteElement*> &el,
                                    ElementTransformation &Tr,
                                    const Array<const Vector *> &elfun,
                                    const Array2D<DenseMatrix *> &elmats);
};

class VectorConvectionNLFIntegrator : public NonlinearFormIntegrator
{
private:
   Coefficient *Q{};
   DenseMatrix dshape, dshapex, EF, gradEF, ELV, elmat_comp;
   Vector shape;
   // PA extension
   Vector pa_data;
   const DofToQuad *maps;         ///< Not owned
   const GeometricFactors *geom;  ///< Not owned
   int dim, ne, nq;
public:
   VectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { }

   VectorConvectionNLFIntegrator() = default;

   static const IntegrationRule &GetRule(const FiniteElement &fe,
                                         ElementTransformation &T);

   virtual void AssembleElementVector(const FiniteElement &el,
                                      ElementTransformation &trans,
                                      const Vector &elfun,
                                      Vector &elvect);

   virtual void AssembleElementGrad(const FiniteElement &el,
                                    ElementTransformation &trans,
                                    const Vector &elfun,
                                    DenseMatrix &elmat);

   using NonlinearFormIntegrator::AssemblePA;

   virtual void AssemblePA(const FiniteElementSpace &fes);

   virtual void AddMultPA(const Vector &x, Vector &y) const;
};

/** @brief This class is used to assemble the convective form of the
nonlinear term arising in the Navier Stokes equations
\f$(u \cdot \nabla v, w )\f$ */
class ConvectiveVectorConvectionNLFIntegrator : public
   VectorConvectionNLFIntegrator
{
private:
   Coefficient *Q{};
   DenseMatrix dshape, dshapex, EF, gradEF, ELV, elmat_comp;
   Vector shape;
public:
   ConvectiveVectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { }

   ConvectiveVectorConvectionNLFIntegrator() = default;

   virtual void AssembleElementGrad(const FiniteElement &el,
                                    ElementTransformation &trans,
                                    const Vector &elfun,
                                    DenseMatrix &elmat);

};
/** @brief This class is used to assemble the skew-symmetric form of the nonlinear
term arising in the Navier Stokes equations \f$.5*(u \cdot \nabla v, w ) -
.5*(u \cdot \nabla w, v )\f$ */
class SkewSymmetricVectorConvectionNLFIntegrator : public
   VectorConvectionNLFIntegrator
{
private:
   Coefficient *Q{};
   DenseMatrix dshape, dshapex, EF, gradEF, ELV, elmat_comp;
   Vector shape;
public:
   SkewSymmetricVectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { }

   SkewSymmetricVectorConvectionNLFIntegrator() = default;

   virtual void AssembleElementGrad(const FiniteElement &el,
                                    ElementTransformation &trans,
                                    const Vector &elfun,
                                    DenseMatrix &elmat);

};

}

#endif
<|endoftext|>


<commit_before>#include "SpaceNavJoystick.hpp"
#include <Geometry2d/Util.hpp>
#include <spnav.h>
#include <iostream>

using namespace std;

REGISTER_CONFIGURABLE(SpaceNavJoystick)

ConfigDouble* SpaceNavJoystick::DribblerPositiveDeadzone;
ConfigDouble* SpaceNavJoystick::DribblerNegativeDeadzone;

SpaceNavJoystick::SpaceNavJoystick() {
    _daemonConnected = false;
    _daemonTried = false;
}

SpaceNavJoystick::~SpaceNavJoystick() { close(); }

bool SpaceNavJoystick::valid() const { return _daemonConnected; }

void SpaceNavJoystick::update() {
    QMutexLocker(&mutex());

    //  try again if we failed last time
    if (!_daemonConnected && !_daemonTried) open();

    //  abort
    if (!_daemonConnected) return;

    _controlValues.kick = false;
    _controlValues.chip = false;

    //  read events from spacenav.  There may be many of them. we're only
    //  interested in the latest motion event, but we want to take note of any
    //  button event that happens
    spnav_event sev;
    while (spnav_poll_event(&sev)) {
        if (sev.type == SPNAV_EVENT_MOTION) {
            //  note: spacenav axes range from -350 to 350

            RJ::Time now = RJ::timestamp();
            const RJ::Time DribbleStepTime = 300000;
            if ((sev.motion.y >
                 SpaceNavJoystick::DribblerPositiveDeadzone->value()) ||
                (sev.motion.y <
                 SpaceNavJoystick::DribblerNegativeDeadzone->value())) {
                //  we don't want to increment it at each iteration, otherwise
                //  it would change dribbler speed unusably quickly.  Instead we
                //  have a delay between each increment
                if ((now - _lastDribbleTime) > DribbleStepTime) {
                    _lastDribbleTime = now;

                    _controlValues.dribblerPower +=
                        0.1 * -sign<int>(sev.motion.y);
                    if (_controlValues.dribblerPower < 0)
                        _controlValues.dribblerPower = 0;
                    if (_controlValues.dribblerPower > 1)
                        _controlValues.dribblerPower = 1;
                }

                _controlValues.translation = Geometry2d::Point(0, 0);
                _controlValues.rotation = 0;
            } else {
                //  spacenav x is side-to-side and z is forward-to-backward
                //  strafe/translate
                //  translation starts out as a normalized value from (-1,-1) to
                //  (1,1)
                _controlValues.translation = Geometry2d::Point(
                    sev.motion.x / 350.0, sev.motion.z / 350.0);

                //  create a mouse deadzone - this keeps the robot from moving
                //  if the joystick is shifted just ever so slightly
                if (_controlValues.translation.mag() < 0.05) {
                    _controlValues.translation = Geometry2d::Point(0, 0);
                }

                //  twisting the spacenav corresponds to sev.motion.ry
                _controlValues.rotation = sev.motion.ry / 350.0;
                if (abs<float>(_controlValues.rotation) < 0.05) {
                    _controlValues.rotation = 0;
                }
            }

            _controlValues.dribble = _controlValues.dribblerPower > 0.01;

        } else {
            //  it's a button event!
            if (sev.button.press) {
                // spacenav button 0 is left, button 1 is right
                // we chose 0 == kicker, 1 == chipper
                if (sev.button.bnum == 1) {
                    _controlValues.chip = true;
                } else {
                    _controlValues.kick = true;
                }
            }
        }

        //  we can't control kick speed with the SpaceNavigator mouse, so
        //  default to half power
        _controlValues.kickPower = 0.5;
    }
}

void SpaceNavJoystick::createConfiguration(Configuration* cfg) {
    DribblerPositiveDeadzone = new ConfigDouble(
        cfg, "SpaceNavJoystick/Positive Dribbler Deadzone", 100);
    DribblerNegativeDeadzone = new ConfigDouble(
        cfg, "SpaceNavJoystick/Negative Dribbler Deadzone", -150);
}

JoystickControlValues SpaceNavJoystick::getJoystickControlValues() {
    return _controlValues;
}

void SpaceNavJoystick::reset() {
    if (_daemonConnected) {
        //  clear any old events in the queue
        spnav_remove_events(SPNAV_EVENT_ANY);
    }

    //  reset commands
    _controlValues = JoystickControlValues();
}

void SpaceNavJoystick::close() { spnav_close(); }

void SpaceNavJoystick::open() {
    _daemonConnected = spnav_open() != -1;
    _daemonTried = true;
    if (!_daemonConnected) {
        cerr << "Unable to connect to spacenav daemon.  Make sure spacenavd is "
                "running if you want to use a 3d mouse to drive"
             << endl;
    } else {
        cout << "Connected to spacenav daemon!  If a 3d mouse is connected, "
                "you can use it to drive"
             << endl;
    }
}
<commit_msg>make pretty<commit_after>#include "SpaceNavJoystick.hpp"
#include <Geometry2d/Util.hpp>
#include <spnav.h>
#include <iostream>

using namespace std;

REGISTER_CONFIGURABLE(SpaceNavJoystick)

ConfigDouble* SpaceNavJoystick::DribblerPositiveDeadzone;
ConfigDouble* SpaceNavJoystick::DribblerNegativeDeadzone;

SpaceNavJoystick::SpaceNavJoystick() {
    _daemonConnected = false;
    _daemonTried = false;
}

SpaceNavJoystick::~SpaceNavJoystick() { close(); }

bool SpaceNavJoystick::valid() const { return _daemonConnected; }

void SpaceNavJoystick::update() {
    QMutexLocker(&mutex());

    //  try again if we failed last time
    if (!_daemonConnected && !_daemonTried) open();

    //  abort
    if (!_daemonConnected) return;

    _controlValues.kick = false;
    _controlValues.chip = false;

    //  read events from spacenav.  There may be many of them. we're only
    //  interested in the latest motion event, but we want to take note of any
    //  button event that happens
    spnav_event sev;
    while (spnav_poll_event(&sev)) {
        if (sev.type == SPNAV_EVENT_MOTION) {
            //  note: spacenav axes range from -350 to 350

            RJ::Time now = RJ::timestamp();
            const RJ::Time DribbleStepTime = 300000;
            if ((sev.motion.y >
                 SpaceNavJoystick::DribblerPositiveDeadzone->value()) ||
                (sev.motion.y <
                 SpaceNavJoystick::DribblerNegativeDeadzone->value())) {
                //  we don't want to increment it at each iteration, otherwise
                //  it would change dribbler speed unusably quickly.  Instead we
                //  have a delay between each increment
                if ((now - _lastDribbleTime) > DribbleStepTime) {
                    _lastDribbleTime = now;

                    _controlValues.dribblerPower +=
                        0.1 * -sign<int>(sev.motion.y);
                    if (_controlValues.dribblerPower < 0)
                        _controlValues.dribblerPower = 0;
                    if (_controlValues.dribblerPower > 1)
                        _controlValues.dribblerPower = 1;
                }

                _controlValues.translation = Geometry2d::Point(0, 0);
                _controlValues.rotation = 0;
            } else {
                //  spacenav x is side-to-side and z is forward-to-backward
                //  strafe/translate
                //  translation starts out as a normalized value from (-1,-1) to
                //  (1,1)
                _controlValues.translation = Geometry2d::Point(
                    sev.motion.x / 350.0, sev.motion.z / 350.0);

                //  create a mouse deadzone - this keeps the robot from moving
                //  if the joystick is shifted just ever so slightly
                if (_controlValues.translation.mag() < 0.05) {
                    _controlValues.translation = Geometry2d::Point(0, 0);
                }

                //  twisting the spacenav corresponds to sev.motion.ry
                _controlValues.rotation = sev.motion.ry / 350.0;
                if (abs<float>(_controlValues.rotation) < 0.05) {
                    _controlValues.rotation = 0;
                }
            }

            _controlValues.dribble = _controlValues.dribblerPower > 0.01;

        } else {
            //  it's a button event!
            if (sev.button.press) {
                // spacenav button 0 is left, button 1 is right
                // we chose 0 == kicker, 1 == chipper
                if (sev.button.bnum == 1) {
                    _controlValues.chip = true;
                } else {
                    _controlValues.kick = true;
                }
            }
        }

        //  we can't control kick speed with the SpaceNavigator mouse, so
        //  default to half power
        _controlValues.kickPower = 0.5;
    }
}

void SpaceNavJoystick::createConfiguration(Configuration* cfg) {
    DribblerPositiveDeadzone = new ConfigDouble(
        cfg, "SpaceNavJoystick/Positive Dribbler Deadzone", 100);
    DribblerNegativeDeadzone = new ConfigDouble(
        cfg, "SpaceNavJoystick/Negative Dribbler Deadzone", -150);
}

JoystickControlValues SpaceNavJoystick::getJoystickControlValues() {
    return _controlValues;
}

void SpaceNavJoystick::reset() {
    if (_daemonConnected) {
        //  clear any old events in the queue
        spnav_remove_events(SPNAV_EVENT_ANY);
    }

    //  reset commands
    _controlValues = JoystickControlValues();
}

void SpaceNavJoystick::close() { spnav_close(); }

void SpaceNavJoystick::open() {
    _daemonConnected = spnav_open() != -1;
    _daemonTried = true;
    if (!_daemonConnected) {
        cerr << "Unable to connect to spacenav daemon.  Make sure spacenavd is "
                "running if you want to use a 3d mouse to drive" << endl;
    } else {
        cout << "Connected to spacenav daemon!  If a 3d mouse is connected, "
                "you can use it to drive" << endl;
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/value_codec.h>
#include <vespa/eval/eval/interpreted_function.h>
#include <vespa/eval/instruction/replace_type_function.h>
#include <vespa/eval/eval/test/tensor_model.hpp>

using namespace vespalib::eval::tensor_function;
using namespace vespalib::eval::test;
using namespace vespalib::eval;
using namespace vespalib;

const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();

TypedCells getCellsRef(const Value &value) {
    return value.cells();
}

struct ChildMock : Leaf {
    bool is_mutable;
    ChildMock(const ValueType &type) : Leaf(type), is_mutable(true) {}
    bool result_is_mutable() const override { return is_mutable; }
    InterpretedFunction::Instruction compile_self(const ValueBuilderFactory &, Stash &) const override { abort(); }
};

struct Fixture {
    Value::UP                                my_value;
    ValueType                                new_type;
    ChildMock                                mock_child;
    ReplaceTypeFunction                      my_fun;
    std::vector<TensorFunction::Child::CREF> children;
    InterpretedFunction::State               state;
    Fixture()
        : my_value(value_from_spec(spec({x(10)}, N()), prod_factory)),
          new_type(ValueType::from_spec("tensor(x[5],y[2])")),
          mock_child(my_value->type()),
          my_fun(new_type, mock_child),
          children(),
          state(prod_factory)
    {
        my_fun.push_children(children);
        state.stack.push_back(*my_value);
        my_fun.compile_self(prod_factory, state.stash).perform(state);
        ASSERT_EQUAL(children.size(), 1u);
        ASSERT_EQUAL(state.stack.size(), 1u);
        ASSERT_TRUE(!new_type.is_error());
    }
};

TEST_F("require that ReplaceTypeFunction works as expected", Fixture()) {
    EXPECT_EQUAL(f1.my_fun.result_type(), f1.new_type);
    EXPECT_EQUAL(f1.my_fun.result_is_mutable(), true);
    f1.mock_child.is_mutable = false;
    EXPECT_EQUAL(f1.my_fun.result_is_mutable(), false);
    EXPECT_EQUAL(&f1.children[0].get().get(), &f1.mock_child);
    EXPECT_EQUAL(getCellsRef(f1.state.stack[0]).data, getCellsRef(*f1.my_value).data);
    EXPECT_EQUAL(getCellsRef(f1.state.stack[0]).size, getCellsRef(*f1.my_value).size);
    EXPECT_EQUAL(f1.state.stack[0].get().type(), f1.new_type);
    fprintf(stderr, "%s\n", f1.my_fun.as_string().c_str());
}

TEST("require that create_compact will collapse duplicate replace operations") {
    Stash stash;
    ValueType type = ValueType::double_type();
    ChildMock leaf(type);
    const ReplaceTypeFunction &a = ReplaceTypeFunction::create_compact(type, leaf, stash);
    const ReplaceTypeFunction &b = ReplaceTypeFunction::create_compact(type, a, stash);
    EXPECT_EQUAL(a.result_type(), type);
    EXPECT_EQUAL(&a.child(), &leaf);
    EXPECT_EQUAL(b.result_type(), type);
    EXPECT_EQUAL(&b.child(), &leaf);
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>use GenSpec in dense_replace_type_function_test<commit_after>// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/value_codec.h>
#include <vespa/eval/eval/interpreted_function.h>
#include <vespa/eval/instruction/replace_type_function.h>
#include <vespa/eval/eval/test/gen_spec.h>

using namespace vespalib::eval::tensor_function;
using namespace vespalib::eval::test;
using namespace vespalib::eval;
using namespace vespalib;

const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();

TypedCells getCellsRef(const Value &value) {
    return value.cells();
}

struct ChildMock : Leaf {
    bool is_mutable;
    ChildMock(const ValueType &type) : Leaf(type), is_mutable(true) {}
    bool result_is_mutable() const override { return is_mutable; }
    InterpretedFunction::Instruction compile_self(const ValueBuilderFactory &, Stash &) const override { abort(); }
};

struct Fixture {
    Value::UP                                my_value;
    ValueType                                new_type;
    ChildMock                                mock_child;
    ReplaceTypeFunction                      my_fun;
    std::vector<TensorFunction::Child::CREF> children;
    InterpretedFunction::State               state;
    Fixture()
        : my_value(value_from_spec(GenSpec().idx("x", 10).gen(), prod_factory)),
          new_type(ValueType::from_spec("tensor(x[5],y[2])")),
          mock_child(my_value->type()),
          my_fun(new_type, mock_child),
          children(),
          state(prod_factory)
    {
        my_fun.push_children(children);
        state.stack.push_back(*my_value);
        my_fun.compile_self(prod_factory, state.stash).perform(state);
        ASSERT_EQUAL(children.size(), 1u);
        ASSERT_EQUAL(state.stack.size(), 1u);
        ASSERT_TRUE(!new_type.is_error());
    }
};

TEST_F("require that ReplaceTypeFunction works as expected", Fixture()) {
    EXPECT_EQUAL(f1.my_fun.result_type(), f1.new_type);
    EXPECT_EQUAL(f1.my_fun.result_is_mutable(), true);
    f1.mock_child.is_mutable = false;
    EXPECT_EQUAL(f1.my_fun.result_is_mutable(), false);
    EXPECT_EQUAL(&f1.children[0].get().get(), &f1.mock_child);
    EXPECT_EQUAL(getCellsRef(f1.state.stack[0]).data, getCellsRef(*f1.my_value).data);
    EXPECT_EQUAL(getCellsRef(f1.state.stack[0]).size, getCellsRef(*f1.my_value).size);
    EXPECT_EQUAL(f1.state.stack[0].get().type(), f1.new_type);
    fprintf(stderr, "%s\n", f1.my_fun.as_string().c_str());
}

TEST("require that create_compact will collapse duplicate replace operations") {
    Stash stash;
    ValueType type = ValueType::double_type();
    ChildMock leaf(type);
    const ReplaceTypeFunction &a = ReplaceTypeFunction::create_compact(type, leaf, stash);
    const ReplaceTypeFunction &b = ReplaceTypeFunction::create_compact(type, a, stash);
    EXPECT_EQUAL(a.result_type(), type);
    EXPECT_EQUAL(&a.child(), &leaf);
    EXPECT_EQUAL(b.result_type(), type);
    EXPECT_EQUAL(&b.child(), &leaf);
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/isteps/istep09/call_proc_io_dccal_done.C $            */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2020                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
/**
 *  @file call_proc_io_dccal_done.C
 *
 *  Support file for IStep: proc_io_dccal_done
 *   Ensure the IO PPE has finished iodccal
 *
 *  HWP_IGNORE_VERSION_CHECK
 *
 */

/******************************************************************************/
// Includes
/******************************************************************************/
#include <stdint.h>
#include <map>

#include <trace/interface.H>
#include <initservice/taskargs.H>
#include <errl/errlentry.H>

#include <isteps/hwpisteperror.H>
#include <errl/errludtarget.H>

#include <initservice/isteps_trace.H>

#include <hwas/common/deconfigGard.H>
#include <hwas/common/hwasCommon.H>

#include <sbe/sbeif.H>

//  targeting support
#include <targeting/common/commontargeting.H>
#include <targeting/common/utilFilter.H>
#include <targeting/common/trace.H>
#include <istepHelperFuncs.H>

#include <pbusLinkSvc.H>

#include <fapi2/target.H>
#include <fapi2/plat_hwp_invoker.H>
#include <errl/errlmanager.H>

#include <p10_io_init_done.H>
#include <sys/time.h>

namespace ISTEP_09
{

using namespace ISTEP;
using namespace ISTEP_ERROR;
using namespace ERRORLOG;
using namespace TARGETING;
using namespace HWAS;

//
//  Wrapper function to call proc_io_dccal_done
//
void* call_proc_io_dccal_done(void* const io_pArgs)
{
    ISTEP_ERROR::IStepError l_stepError;
    TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace, "call_proc_io_dccal_done entry");

    //@FIXME-RTC:258548-SW501880-Add extra delay here to handle slow PPE
    nanosleep( 5, 0 ); //Wait 5 seconds

    // Get all functional proc chip targets
    TARGETING::TargetHandleList l_cpuTargetList;
    TARGETING::getAllChips(l_cpuTargetList, TARGETING::TYPE_PROC);

    for (const auto l_handle : l_cpuTargetList)
    {
        fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> l_procChip(l_handle);

        errlHndl_t l_errl = nullptr;
        FAPI_INVOKE_HWP(l_errl,
                        p10_io_init_done,
                        l_procChip);

        if (l_errl)
        {
            TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                      "call_proc_io_dccal_done: p10_io_init_done failed on PROC HUID 0x%08x (reason: 0x%x). "
                      TRACE_ERR_FMT,
                      get_huid(l_handle),
                      l_errl->reasonCode(),
                      TRACE_ERR_ARGS(l_errl));

            captureError(l_errl, l_stepError, HWPF_COMP_ID, l_handle);
        }
    }

    TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace, "call_proc_io_dccal_done exit" );

    return l_stepError.getErrorHandle();
}

}
<commit_msg>Remove extra delay before proc_io_dccal_done<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/isteps/istep09/call_proc_io_dccal_done.C $            */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2021                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
/**
 *  @file call_proc_io_dccal_done.C
 *
 *  Support file for IStep: proc_io_dccal_done
 *   Ensure the IO PPE has finished iodccal
 *
 *  HWP_IGNORE_VERSION_CHECK
 *
 */

/******************************************************************************/
// Includes
/******************************************************************************/
#include <stdint.h>
#include <map>

#include <trace/interface.H>
#include <initservice/taskargs.H>
#include <errl/errlentry.H>

#include <isteps/hwpisteperror.H>
#include <errl/errludtarget.H>

#include <initservice/isteps_trace.H>

#include <hwas/common/deconfigGard.H>
#include <hwas/common/hwasCommon.H>

#include <sbe/sbeif.H>

//  targeting support
#include <targeting/common/commontargeting.H>
#include <targeting/common/utilFilter.H>
#include <targeting/common/trace.H>
#include <istepHelperFuncs.H>

#include <pbusLinkSvc.H>

#include <fapi2/target.H>
#include <fapi2/plat_hwp_invoker.H>
#include <errl/errlmanager.H>

#include <p10_io_init_done.H>
#include <sys/time.h>

namespace ISTEP_09
{

using namespace ISTEP;
using namespace ISTEP_ERROR;
using namespace ERRORLOG;
using namespace TARGETING;
using namespace HWAS;

//
//  Wrapper function to call proc_io_dccal_done
//
void* call_proc_io_dccal_done(void* const io_pArgs)
{
    ISTEP_ERROR::IStepError l_stepError;
    TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace, "call_proc_io_dccal_done entry");

    // Get all functional proc chip targets
    TARGETING::TargetHandleList l_cpuTargetList;
    TARGETING::getAllChips(l_cpuTargetList, TARGETING::TYPE_PROC);

    for (const auto l_handle : l_cpuTargetList)
    {
        fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> l_procChip(l_handle);

        errlHndl_t l_errl = nullptr;
        FAPI_INVOKE_HWP(l_errl,
                        p10_io_init_done,
                        l_procChip);

        if (l_errl)
        {
            TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                      "call_proc_io_dccal_done: p10_io_init_done failed on PROC HUID 0x%08x (reason: 0x%x). "
                      TRACE_ERR_FMT,
                      get_huid(l_handle),
                      l_errl->reasonCode(),
                      TRACE_ERR_ARGS(l_errl));

            captureError(l_errl, l_stepError, HWPF_COMP_ID, l_handle);
        }
    }

    TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace, "call_proc_io_dccal_done exit" );

    return l_stepError.getErrorHandle();
}

}
<|endoftext|>