<commit_before>/*
 *
 * Copyright 2015 gRPC authors.
 *
 * 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 "src/core/ext/transport/chttp2/client/chttp2_connector.h"

#include <grpc/grpc.h>

#include <string.h>

#include <grpc/slice_buffer.h>
#include <grpc/support/alloc.h>
#include <grpc/support/string_util.h>

#include "src/core/ext/filters/client_channel/connector.h"
#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
#include "src/core/ext/filters/client_channel/subchannel.h"
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/handshaker.h"
#include "src/core/lib/channel/handshaker_registry.h"
#include "src/core/lib/iomgr/tcp_client.h"
#include "src/core/lib/slice/slice_internal.h"

typedef struct {
  grpc_connector base;

  gpr_mu mu;
  gpr_refcount refs;

  bool shutdown;
  bool connecting;

  grpc_closure *notify;
  grpc_connect_in_args args;
  grpc_connect_out_args *result;

  grpc_endpoint *endpoint;  // Non-NULL until handshaking starts.

  grpc_closure connected;

  grpc_handshake_manager *handshake_mgr;
} chttp2_connector;

static void chttp2_connector_ref(grpc_connector *con) {
  chttp2_connector *c = (chttp2_connector *)con;
  gpr_ref(&c->refs);
}

static void chttp2_connector_unref(grpc_exec_ctx *exec_ctx,
                                   grpc_connector *con) {
  chttp2_connector *c = (chttp2_connector *)con;
  if (gpr_unref(&c->refs)) {
    gpr_mu_destroy(&c->mu);
    // If handshaking is not yet in progress, destroy the endpoint.
    // Otherwise, the handshaker will do this for us.
    if (c->endpoint != NULL) grpc_endpoint_destroy(exec_ctx, c->endpoint);
    gpr_free(c);
  }
}

static void chttp2_connector_shutdown(grpc_exec_ctx *exec_ctx,
                                      grpc_connector *con, grpc_error *why) {
  chttp2_connector *c = (chttp2_connector *)con;
  gpr_mu_lock(&c->mu);
  c->shutdown = true;
  if (c->handshake_mgr != NULL) {
    grpc_handshake_manager_shutdown(exec_ctx, c->handshake_mgr,
                                    GRPC_ERROR_REF(why));
  }
  // If handshaking is not yet in progress, shutdown the endpoint.
  // Otherwise, the handshaker will do this for us.
  if (!c->connecting && c->endpoint != NULL) {
    grpc_endpoint_shutdown(exec_ctx, c->endpoint, GRPC_ERROR_REF(why));
  }
  gpr_mu_unlock(&c->mu);
  GRPC_ERROR_UNREF(why);
}

static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
                              grpc_error *error) {
  grpc_handshaker_args *args = (grpc_handshaker_args *)arg;
  chttp2_connector *c = (chttp2_connector *)args->user_data;
  gpr_mu_lock(&c->mu);
  if (error != GRPC_ERROR_NONE || c->shutdown) {
    if (error == GRPC_ERROR_NONE) {
      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown");
      // We were shut down after handshaking completed successfully, so
      // destroy the endpoint here.
      // TODO(ctiller): It is currently necessary to shutdown endpoints
      // before destroying them, even if we know that there are no
      // pending read/write callbacks.  This should be fixed, at which
      // point this can be removed.
      grpc_endpoint_shutdown(exec_ctx, args->endpoint, GRPC_ERROR_REF(error));
      grpc_endpoint_destroy(exec_ctx, args->endpoint);
      grpc_channel_args_destroy(exec_ctx, args->args);
      grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer);
      gpr_free(args->read_buffer);
    } else {
      error = GRPC_ERROR_REF(error);
    }
    memset(c->result, 0, sizeof(*c->result));
  } else {
    c->result->transport =
        grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 1);
    GPR_ASSERT(c->result->transport);
    grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport,
                                        args->read_buffer);
    c->result->channel_args = args->args;
  }
  grpc_closure *notify = c->notify;
  c->notify = NULL;
  GRPC_CLOSURE_SCHED(exec_ctx, notify, error);
  grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr);
  c->handshake_mgr = NULL;
  gpr_mu_unlock(&c->mu);
  chttp2_connector_unref(exec_ctx, (grpc_connector *)c);
}

static void start_handshake_locked(grpc_exec_ctx *exec_ctx,
                                   chttp2_connector *c) {
  c->handshake_mgr = grpc_handshake_manager_create();
  grpc_handshakers_add(exec_ctx, HANDSHAKER_CLIENT, c->args.channel_args,
                       c->handshake_mgr);
  grpc_handshake_manager_do_handshake(
      exec_ctx, c->handshake_mgr, c->endpoint, c->args.channel_args,
      c->args.deadline, NULL /* acceptor */, on_handshake_done, c);
  c->endpoint = NULL;  // Endpoint handed off to handshake manager.
}

static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
  chttp2_connector *c = (chttp2_connector *)arg;
  gpr_mu_lock(&c->mu);
  GPR_ASSERT(c->connecting);
  c->connecting = false;
  if (error != GRPC_ERROR_NONE || c->shutdown) {
    if (error == GRPC_ERROR_NONE) {
      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown");
    } else {
      error = GRPC_ERROR_REF(error);
    }
    memset(c->result, 0, sizeof(*c->result));
    grpc_closure *notify = c->notify;
    c->notify = NULL;
    GRPC_CLOSURE_SCHED(exec_ctx, notify, error);
    if (c->endpoint != NULL) {
      grpc_endpoint_shutdown(exec_ctx, c->endpoint, GRPC_ERROR_REF(error));
    }
    gpr_mu_unlock(&c->mu);
    chttp2_connector_unref(exec_ctx, (grpc_connector *)arg);
  } else {
    GPR_ASSERT(c->endpoint != NULL);
    start_handshake_locked(exec_ctx, c);
    gpr_mu_unlock(&c->mu);
  }
}

static void chttp2_connector_connect(grpc_exec_ctx *exec_ctx,
                                     grpc_connector *con,
                                     const grpc_connect_in_args *args,
                                     grpc_connect_out_args *result,
                                     grpc_closure *notify) {
  chttp2_connector *c = (chttp2_connector *)con;
  grpc_resolved_address addr;
  grpc_get_subchannel_address_arg(exec_ctx, args->channel_args, &addr);
  gpr_mu_lock(&c->mu);
  GPR_ASSERT(c->notify == NULL);
  c->notify = notify;
  c->args = *args;
  c->result = result;
  GPR_ASSERT(c->endpoint == NULL);
  chttp2_connector_ref(con);  // Ref taken for callback.
  GRPC_CLOSURE_INIT(&c->connected, connected, c, grpc_schedule_on_exec_ctx);
  GPR_ASSERT(!c->connecting);
  c->connecting = true;
  grpc_tcp_client_connect(exec_ctx, &c->connected, &c->endpoint,
                          args->interested_parties, args->channel_args, &addr,
                          args->deadline);
  gpr_mu_unlock(&c->mu);
}

static const grpc_connector_vtable chttp2_connector_vtable = {
    chttp2_connector_ref, chttp2_connector_unref, chttp2_connector_shutdown,
    chttp2_connector_connect};

grpc_connector *grpc_chttp2_connector_create() {
  chttp2_connector *c = (chttp2_connector *)gpr_zalloc(sizeof(*c));
  c->base.vtable = &chttp2_connector_vtable;
  gpr_mu_init(&c->mu);
  gpr_ref_init(&c->refs, 1);
  return &c->base;
}
<commit_msg>Fix uds handshake<commit_after>/*
 *
 * Copyright 2015 gRPC authors.
 *
 * 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 "src/core/ext/transport/chttp2/client/chttp2_connector.h"

#include <grpc/grpc.h>

#include <string.h>

#include <grpc/slice_buffer.h>
#include <grpc/support/alloc.h>
#include <grpc/support/string_util.h>

#include "src/core/ext/filters/client_channel/connector.h"
#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
#include "src/core/ext/filters/client_channel/subchannel.h"
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/handshaker.h"
#include "src/core/lib/channel/handshaker_registry.h"
#include "src/core/lib/iomgr/tcp_client.h"
#include "src/core/lib/slice/slice_internal.h"

typedef struct {
  grpc_connector base;

  gpr_mu mu;
  gpr_refcount refs;

  bool shutdown;
  bool connecting;

  grpc_closure *notify;
  grpc_connect_in_args args;
  grpc_connect_out_args *result;

  grpc_endpoint *endpoint;  // Non-NULL until handshaking starts.

  grpc_closure connected;

  grpc_handshake_manager *handshake_mgr;
} chttp2_connector;

static void chttp2_connector_ref(grpc_connector *con) {
  chttp2_connector *c = (chttp2_connector *)con;
  gpr_ref(&c->refs);
}

static void chttp2_connector_unref(grpc_exec_ctx *exec_ctx,
                                   grpc_connector *con) {
  chttp2_connector *c = (chttp2_connector *)con;
  if (gpr_unref(&c->refs)) {
    gpr_mu_destroy(&c->mu);
    // If handshaking is not yet in progress, destroy the endpoint.
    // Otherwise, the handshaker will do this for us.
    if (c->endpoint != NULL) grpc_endpoint_destroy(exec_ctx, c->endpoint);
    gpr_free(c);
  }
}

static void chttp2_connector_shutdown(grpc_exec_ctx *exec_ctx,
                                      grpc_connector *con, grpc_error *why) {
  chttp2_connector *c = (chttp2_connector *)con;
  gpr_mu_lock(&c->mu);
  c->shutdown = true;
  if (c->handshake_mgr != NULL) {
    grpc_handshake_manager_shutdown(exec_ctx, c->handshake_mgr,
                                    GRPC_ERROR_REF(why));
  }
  // If handshaking is not yet in progress, shutdown the endpoint.
  // Otherwise, the handshaker will do this for us.
  if (!c->connecting && c->endpoint != NULL) {
    grpc_endpoint_shutdown(exec_ctx, c->endpoint, GRPC_ERROR_REF(why));
  }
  gpr_mu_unlock(&c->mu);
  GRPC_ERROR_UNREF(why);
}

static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
                              grpc_error *error) {
  grpc_handshaker_args *args = (grpc_handshaker_args *)arg;
  chttp2_connector *c = (chttp2_connector *)args->user_data;
  gpr_mu_lock(&c->mu);
  if (error != GRPC_ERROR_NONE || c->shutdown) {
    if (error == GRPC_ERROR_NONE) {
      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown");
      // We were shut down after handshaking completed successfully, so
      // destroy the endpoint here.
      // TODO(ctiller): It is currently necessary to shutdown endpoints
      // before destroying them, even if we know that there are no
      // pending read/write callbacks.  This should be fixed, at which
      // point this can be removed.
      grpc_endpoint_shutdown(exec_ctx, args->endpoint, GRPC_ERROR_REF(error));
      grpc_endpoint_destroy(exec_ctx, args->endpoint);
      grpc_channel_args_destroy(exec_ctx, args->args);
      grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer);
      gpr_free(args->read_buffer);
    } else {
      error = GRPC_ERROR_REF(error);
    }
    memset(c->result, 0, sizeof(*c->result));
  } else {
    c->result->transport =
        grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 1);
    GPR_ASSERT(c->result->transport);
    grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport,
                                        args->read_buffer);
    c->result->channel_args = args->args;
  }
  grpc_closure *notify = c->notify;
  c->notify = NULL;
  GRPC_CLOSURE_SCHED(exec_ctx, notify, error);
  grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr);
  c->handshake_mgr = NULL;
  gpr_mu_unlock(&c->mu);
  chttp2_connector_unref(exec_ctx, (grpc_connector *)c);
}

static void start_handshake_locked(grpc_exec_ctx *exec_ctx,
                                   chttp2_connector *c) {
  c->handshake_mgr = grpc_handshake_manager_create();
  grpc_handshakers_add(exec_ctx, HANDSHAKER_CLIENT, c->args.channel_args,
                       c->handshake_mgr);
  grpc_endpoint_add_to_pollset_set(exec_ctx, c->endpoint,
                                   c->args.interested_parties);
  grpc_handshake_manager_do_handshake(
      exec_ctx, c->handshake_mgr, c->endpoint, c->args.channel_args,
      c->args.deadline, NULL /* acceptor */, on_handshake_done, c);
  c->endpoint = NULL;  // Endpoint handed off to handshake manager.
}

static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
  chttp2_connector *c = (chttp2_connector *)arg;
  gpr_mu_lock(&c->mu);
  GPR_ASSERT(c->connecting);
  c->connecting = false;
  if (error != GRPC_ERROR_NONE || c->shutdown) {
    if (error == GRPC_ERROR_NONE) {
      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown");
    } else {
      error = GRPC_ERROR_REF(error);
    }
    memset(c->result, 0, sizeof(*c->result));
    grpc_closure *notify = c->notify;
    c->notify = NULL;
    GRPC_CLOSURE_SCHED(exec_ctx, notify, error);
    if (c->endpoint != NULL) {
      grpc_endpoint_shutdown(exec_ctx, c->endpoint, GRPC_ERROR_REF(error));
    }
    gpr_mu_unlock(&c->mu);
    chttp2_connector_unref(exec_ctx, (grpc_connector *)arg);
  } else {
    GPR_ASSERT(c->endpoint != NULL);
    start_handshake_locked(exec_ctx, c);
    gpr_mu_unlock(&c->mu);
  }
}

static void chttp2_connector_connect(grpc_exec_ctx *exec_ctx,
                                     grpc_connector *con,
                                     const grpc_connect_in_args *args,
                                     grpc_connect_out_args *result,
                                     grpc_closure *notify) {
  chttp2_connector *c = (chttp2_connector *)con;
  grpc_resolved_address addr;
  grpc_get_subchannel_address_arg(exec_ctx, args->channel_args, &addr);
  gpr_mu_lock(&c->mu);
  GPR_ASSERT(c->notify == NULL);
  c->notify = notify;
  c->args = *args;
  c->result = result;
  GPR_ASSERT(c->endpoint == NULL);
  chttp2_connector_ref(con);  // Ref taken for callback.
  GRPC_CLOSURE_INIT(&c->connected, connected, c, grpc_schedule_on_exec_ctx);
  GPR_ASSERT(!c->connecting);
  c->connecting = true;
  grpc_tcp_client_connect(exec_ctx, &c->connected, &c->endpoint,
                          args->interested_parties, args->channel_args, &addr,
                          args->deadline);
  gpr_mu_unlock(&c->mu);
}

static const grpc_connector_vtable chttp2_connector_vtable = {
    chttp2_connector_ref, chttp2_connector_unref, chttp2_connector_shutdown,
    chttp2_connector_connect};

grpc_connector *grpc_chttp2_connector_create() {
  chttp2_connector *c = (chttp2_connector *)gpr_zalloc(sizeof(*c));
  c->base.vtable = &chttp2_connector_vtable;
  gpr_mu_init(&c->mu);
  gpr_ref_init(&c->refs, 1);
  return &c->base;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
//
//             ----------------------------------------------
//             Twist Miniapp:  Generate simple twisted meshes
//             ----------------------------------------------
//
// This miniapp generates two types of Toroidal meshes; one with triangular
// cross sections and one with square cross sections.  It works by defining a
// stack of individual elements and bending them so that the bottom and top of
// the stack can be joined to form a torus.  The stack can also be twisted so
// that the vertices of the bottom and top can be joined with any integer
// offset.
//
// Compile with: make toroid
//
// Sample runs:  toroid
//               toroid -nphi 6
//               toroid -ns 1
//               toroid -ns 0 -t0 -30
//               toroid -R 2 -r 1 -ns 3
//               toroid -R 2 -r 1 -ns -3
//               toroid -R 2 -r 1 -ns 3 -e 1
//               toroid -R 2 -r 1 -ns 3 -e 1 -rs 1
//               toroid -nphi 2 -ns 10 -e 1 -o 4

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

static Element::Type el_type_ = Element::WEDGE;
static int    order_ = 3;
static int    nz_    = 3;
static int    ns_    = 0;
static double a_     = 1.0;
static double b_     = 1.0;
static double c_     = 3.0;

void pts(int iphi, int t, double x[]);
void trans(const Vector &x, Vector &p);

int main(int argc, char *argv[])
{
   int ser_ref_levels = 0;
   int el_type = 8;
   bool per_mesh = false;
   bool dg_mesh  = false;
   bool visualization = true;

   OptionsParser args(argc, argv);
   args.AddOption(&nz_, "-nz", "--num-elements-z",
                  "Number of elements in z-direction.");
   args.AddOption(&ns_, "-ns", "--num-shifts",
                  "Number of shifts.");
   args.AddOption(&order_, "-o", "--mesh-order",
                  "Order (polynomial degree) of the mesh elements.");
   args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
                  "Number of times to refine the mesh uniformly in serial.");
   args.AddOption(&a_, "-a", "--base-x",
                  "Width of the base in x-direction.");
   args.AddOption(&b_, "-b", "--base-y",
                  "Width of the base in y-direction.");
   args.AddOption(&c_, "-c", "--height",
                  "Height in z-direction.");
   args.AddOption(&el_type, "-e", "--element-type",
                  "Element type: 4 - Tetrahedron, 6 - Wedge, 8 - Hexahedron.");
   args.AddOption(&per_mesh, "-pm", "--periodic-mesh",
                  "-no-pm", "--non-periodic-mesh",
                  "Enforce periodicity in z-direction "
                  "(requires discontinuous mesh).");
   args.AddOption(&dg_mesh, "-dm", "--discont-mesh", "-cm", "--cont-mesh",
                  "Use discontinuous or continuous space for the mesh nodes.");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good())
   {
      args.PrintUsage(cout);
      return 1;
   }
   args.PrintOptions(cout);

   // The output mesh could be tetrahedra, hexahedra, or prisms
   switch (el_type)
   {
      case 4:
         el_type_ = Element::TETRAHEDRON;
         break;
      case 6:
         el_type_ = Element::WEDGE;
         break;
      case 8:
         el_type_ = Element::HEXAHEDRON;
         break;
      default:
         cout << "Unsupported element type" << endl;
         exit(1);
         break;
   }

   // Define the mesh
   Mesh *mesh = new Mesh(1, 1, nz_, el_type_, false, a_, b_, c_, false);

   // Promote to high order mesh and transform into a twisted shape
   if (order_ > 1 || dg_mesh || per_mesh)
   {
      mesh->SetCurvature(order_, dg_mesh || per_mesh, 3, Ordering::byVDIM);
   }
   if (ns_ != 0 )
   {
      mesh->Transform(trans);
   }

   while (per_mesh)
   {
      // Verify geometric compatibility
      if (ns_ % 2 == 1 && fabs(a_ - b_) > 1e-6 * a_)
      {
         cout << "Base is rectangular so number of shifts must be even "
              << "for a periodic mesh!" << endl;
         exit(1);
      }

      // Verify topological compatibility
      if (ns_ % 2 == 1 && (el_type_ == Element::TETRAHEDRON ||
                           el_type_ == Element::WEDGE))
      {
         cout << "Diagonal cuts on the base and top must line up "
              << "for a periodic mesh!" << endl;
         exit(1);
      }

      int nnode = 4;
      int noff = (ns_ >= 0) ? 0 : (nnode * (1 - ns_ / nnode));

      Array<int> v2v(mesh->GetNV());
      for (int i = 0; i < v2v.Size() - nnode; i++)
      {
         v2v[i] = i;
      }
      // identify vertices at the extremes of the stack
      switch ((noff + ns_) % nnode)
      {
         case 0:
            v2v[v2v.Size() - nnode + 0] = 0;
            v2v[v2v.Size() - nnode + 1] = 1;
            v2v[v2v.Size() - nnode + 2] = 2;
            v2v[v2v.Size() - nnode + 3] = 3;
            break;
         case 1:
            v2v[v2v.Size() - nnode + 0] = 2;
            v2v[v2v.Size() - nnode + 1] = 0;
            v2v[v2v.Size() - nnode + 2] = 3;
            v2v[v2v.Size() - nnode + 3] = 1;
            break;
         case 2:
            v2v[v2v.Size() - nnode + 0] = 3;
            v2v[v2v.Size() - nnode + 1] = 2;
            v2v[v2v.Size() - nnode + 2] = 1;
            v2v[v2v.Size() - nnode + 3] = 0;
            break;
         case 3:
            v2v[v2v.Size() - nnode + 0] = 1;
            v2v[v2v.Size() - nnode + 1] = 3;
            v2v[v2v.Size() - nnode + 2] = 0;
            v2v[v2v.Size() - nnode + 3] = 2;
            break;
      }
      // renumber elements
      for (int i = 0; i < mesh->GetNE(); i++)
      {
         Element *el = mesh->GetElement(i);
         int *v = el->GetVertices();
         int nv = el->GetNVertices();
         for (int j = 0; j < nv; j++)
         {
            v[j] = v2v[v[j]];
         }
      }
      // renumber boundary elements
      for (int i = 0; i < mesh->GetNBE(); i++)
      {
         Element *el = mesh->GetBdrElement(i);
         int *v = el->GetVertices();
         int nv = el->GetNVertices();
         for (int j = 0; j < nv; j++)
         {
            v[j] = v2v[v[j]];
         }
      }
      mesh->RemoveUnusedVertices();
      mesh->RemoveInternalBoundaries();

      break;
   }

   // Refine the mesh if desired
   for (int lev = 0; lev < ser_ref_levels; lev++)
   {
      mesh->UniformRefinement();
   }

   // Output the resulting mesh to a file
   {
      ostringstream oss;
      if (el_type_ == Element::TETRAHEDRON)
      {
         oss << "twist-tet";
      }
      else if (el_type_ == Element::WEDGE)
      {
         oss << "twist-wedge";
      }
      else
      {
         oss << "twist-hex";
      }
      oss << "-o" << order_ << "-s" << ns_;
      if (ser_ref_levels > 0)
      {
         oss << "-r" << ser_ref_levels;
      }
      if (per_mesh)
      {
         oss << "-p";
      }
      else if (dg_mesh)
      {
         oss << "-d";
      }
      else
      {
         oss << "-c";
      }

      oss << ".mesh";
      ofstream ofs(oss.str().c_str());
      ofs.precision(8);
      mesh->Print(ofs);
      ofs.close();
   }

   // Output the resulting mesh to GLVis
   if (visualization)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      socketstream sol_sock(vishost, visport);
      sol_sock.precision(8);
      sol_sock << "mesh\n" << *mesh << flush;
   }

   // Clean up and exit
   delete mesh;
   return 0;
}

void trans(const Vector &x, Vector &p)
{
   double z = x[2];
   double phi = 0.5 * M_PI * ns_ * z / c_;
   double cp = cos(phi);
   double sp = sin(phi);

   p[0] = 0.5 * a_ + (x[0] - 0.5 * a_) * cp - (x[1] - 0.5 * b_) * sp;
   p[1] = 0.5 * b_ + (x[0] - 0.5 * a_) * sp + (x[1] - 0.5 * b_) * cp;
   p[2] = z;
}
<commit_msg>Updating miniapp comments<commit_after>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
//
//             ----------------------------------------------
//             Twist Miniapp:  Generate simple twisted meshes
//             ----------------------------------------------
//
// This miniapp generates simple periodic meshes to demonstrate MFEM's
// implementation of periodic domains.  MFEM's strategy is to use a
// discontinuous vector field to define the mesh coordinates on a
// topologically periodic mesh.  It works by defining a stack of
// individual elements and stitching together the top and bottom of
// the mesh.  The stack can also be twisted so that the vertices of
// the bottom and top can be joined with any integer offset (for
// tetrahedral and wedge meshes only even offsets are supported).
//
// Compile with: make twist
//
// Sample runs:  twist
//               twist -ns 2
//               twist -ns -2
//               twist -ns 2 -e 4 -pm
//               twist -ns 2 -e 6 -pm
//               twist -ns 3 -e 8 -pm
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

static Element::Type el_type_ = Element::WEDGE;
static int    order_ = 3;
static int    nz_    = 3;
static int    ns_    = 0;
static double a_     = 1.0;
static double b_     = 1.0;
static double c_     = 3.0;

void pts(int iphi, int t, double x[]);
void trans(const Vector &x, Vector &p);

int main(int argc, char *argv[])
{
   int ser_ref_levels = 0;
   int el_type = 8;
   bool per_mesh = false;
   bool dg_mesh  = false;
   bool visualization = true;

   OptionsParser args(argc, argv);
   args.AddOption(&nz_, "-nz", "--num-elements-z",
                  "Number of elements in z-direction.");
   args.AddOption(&ns_, "-ns", "--num-shifts",
                  "Number of shifts.");
   args.AddOption(&order_, "-o", "--mesh-order",
                  "Order (polynomial degree) of the mesh elements.");
   args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
                  "Number of times to refine the mesh uniformly in serial.");
   args.AddOption(&a_, "-a", "--base-x",
                  "Width of the base in x-direction.");
   args.AddOption(&b_, "-b", "--base-y",
                  "Width of the base in y-direction.");
   args.AddOption(&c_, "-c", "--height",
                  "Height in z-direction.");
   args.AddOption(&el_type, "-e", "--element-type",
                  "Element type: 4 - Tetrahedron, 6 - Wedge, 8 - Hexahedron.");
   args.AddOption(&per_mesh, "-pm", "--periodic-mesh",
                  "-no-pm", "--non-periodic-mesh",
                  "Enforce periodicity in z-direction "
                  "(requires discontinuous mesh).");
   args.AddOption(&dg_mesh, "-dm", "--discont-mesh", "-cm", "--cont-mesh",
                  "Use discontinuous or continuous space for the mesh nodes.");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good())
   {
      args.PrintUsage(cout);
      return 1;
   }
   args.PrintOptions(cout);

   // The output mesh could be tetrahedra, hexahedra, or prisms
   switch (el_type)
   {
      case 4:
         el_type_ = Element::TETRAHEDRON;
         break;
      case 6:
         el_type_ = Element::WEDGE;
         break;
      case 8:
         el_type_ = Element::HEXAHEDRON;
         break;
      default:
         cout << "Unsupported element type" << endl;
         exit(1);
         break;
   }

   // Define the mesh
   Mesh *mesh = new Mesh(1, 1, nz_, el_type_, false, a_, b_, c_, false);

   // Promote to high order mesh and transform into a twisted shape
   if (order_ > 1 || dg_mesh || per_mesh)
   {
      mesh->SetCurvature(order_, dg_mesh || per_mesh, 3, Ordering::byVDIM);
   }
   if (ns_ != 0 )
   {
      mesh->Transform(trans);
   }

   while (per_mesh)
   {
      // Verify geometric compatibility
      if (ns_ % 2 == 1 && fabs(a_ - b_) > 1e-6 * a_)
      {
         cout << "Base is rectangular so number of shifts must be even "
              << "for a periodic mesh!" << endl;
         exit(1);
      }

      // Verify topological compatibility
      if (ns_ % 2 == 1 && (el_type_ == Element::TETRAHEDRON ||
                           el_type_ == Element::WEDGE))
      {
         cout << "Diagonal cuts on the base and top must line up "
              << "for a periodic mesh!" << endl;
         exit(1);
      }

      int nnode = 4;
      int noff = (ns_ >= 0) ? 0 : (nnode * (1 - ns_ / nnode));

      Array<int> v2v(mesh->GetNV());
      for (int i = 0; i < v2v.Size() - nnode; i++)
      {
         v2v[i] = i;
      }
      // identify vertices at the extremes of the stack
      switch ((noff + ns_) % nnode)
      {
         case 0:
            v2v[v2v.Size() - nnode + 0] = 0;
            v2v[v2v.Size() - nnode + 1] = 1;
            v2v[v2v.Size() - nnode + 2] = 2;
            v2v[v2v.Size() - nnode + 3] = 3;
            break;
         case 1:
            v2v[v2v.Size() - nnode + 0] = 2;
            v2v[v2v.Size() - nnode + 1] = 0;
            v2v[v2v.Size() - nnode + 2] = 3;
            v2v[v2v.Size() - nnode + 3] = 1;
            break;
         case 2:
            v2v[v2v.Size() - nnode + 0] = 3;
            v2v[v2v.Size() - nnode + 1] = 2;
            v2v[v2v.Size() - nnode + 2] = 1;
            v2v[v2v.Size() - nnode + 3] = 0;
            break;
         case 3:
            v2v[v2v.Size() - nnode + 0] = 1;
            v2v[v2v.Size() - nnode + 1] = 3;
            v2v[v2v.Size() - nnode + 2] = 0;
            v2v[v2v.Size() - nnode + 3] = 2;
            break;
      }
      // renumber elements
      for (int i = 0; i < mesh->GetNE(); i++)
      {
         Element *el = mesh->GetElement(i);
         int *v = el->GetVertices();
         int nv = el->GetNVertices();
         for (int j = 0; j < nv; j++)
         {
            v[j] = v2v[v[j]];
         }
      }
      // renumber boundary elements
      for (int i = 0; i < mesh->GetNBE(); i++)
      {
         Element *el = mesh->GetBdrElement(i);
         int *v = el->GetVertices();
         int nv = el->GetNVertices();
         for (int j = 0; j < nv; j++)
         {
            v[j] = v2v[v[j]];
         }
      }
      mesh->RemoveUnusedVertices();
      mesh->RemoveInternalBoundaries();

      break;
   }

   // Refine the mesh if desired
   for (int lev = 0; lev < ser_ref_levels; lev++)
   {
      mesh->UniformRefinement();
   }

   // Output the resulting mesh to a file
   {
      ostringstream oss;
      if (el_type_ == Element::TETRAHEDRON)
      {
         oss << "twist-tet";
      }
      else if (el_type_ == Element::WEDGE)
      {
         oss << "twist-wedge";
      }
      else
      {
         oss << "twist-hex";
      }
      oss << "-o" << order_ << "-s" << ns_;
      if (ser_ref_levels > 0)
      {
         oss << "-r" << ser_ref_levels;
      }
      if (per_mesh)
      {
         oss << "-p";
      }
      else if (dg_mesh)
      {
         oss << "-d";
      }
      else
      {
         oss << "-c";
      }

      oss << ".mesh";
      ofstream ofs(oss.str().c_str());
      ofs.precision(8);
      mesh->Print(ofs);
      ofs.close();
   }

   // Output the resulting mesh to GLVis
   if (visualization)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      socketstream sol_sock(vishost, visport);
      sol_sock.precision(8);
      sol_sock << "mesh\n" << *mesh << flush;
   }

   // Clean up and exit
   delete mesh;
   return 0;
}

void trans(const Vector &x, Vector &p)
{
   double z = x[2];
   double phi = 0.5 * M_PI * ns_ * z / c_;
   double cp = cos(phi);
   double sp = sin(phi);

   p[0] = 0.5 * a_ + (x[0] - 0.5 * a_) * cp - (x[1] - 0.5 * b_) * sp;
   p[1] = 0.5 * b_ + (x[0] - 0.5 * a_) * sp + (x[1] - 0.5 * b_) * cp;
   p[2] = z;
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <support/lockedpool.h>
#include <support/cleanse.h>

#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif

#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <sys/mman.h> // for mmap
#include <sys/resource.h> // for getrlimit
#include <limits.h> // for PAGESIZE
#include <unistd.h> // for sysconf
#endif

#include <algorithm>

LockedPoolManager* LockedPoolManager::_instance = nullptr;
std::once_flag LockedPoolManager::init_flag;

/*******************************************************************************/
// Utilities
//
/** Align up to power of 2 */
static inline size_t align_up(size_t x, size_t align)
{
    return (x + align - 1) & ~(align - 1);
}

/*******************************************************************************/
// Implementation: Arena

Arena::Arena(void *base_in, size_t size_in, size_t alignment_in):
    base(static_cast<char*>(base_in)), end(static_cast<char*>(base_in) + size_in), alignment(alignment_in)
{
    // Start with one free chunk that covers the entire arena
    auto it = size_to_free_chunk.emplace(size_in, base);
    chunks_free.emplace(base, it);
    chunks_free_end.emplace(base + size_in, it);
}

Arena::~Arena()
{
}

void* Arena::alloc(size_t size)
{
    // Round to next multiple of alignment
    size = align_up(size, alignment);

    // Don't handle zero-sized chunks
    if (size == 0)
        return nullptr;

    // Pick a large enough free-chunk. Returns an iterator pointing to the first element that is not less than key.
    // This allocation strategy is best-fit. According to "Dynamic Storage Allocation: A Survey and Critical Review",
    // Wilson et. al. 1995, http://www.scs.stanford.edu/14wi-cs140/sched/readings/wilson.pdf, best-fit and first-fit
    // policies seem to work well in practice.
    auto size_ptr_it = size_to_free_chunk.lower_bound(size);
    if (size_ptr_it == size_to_free_chunk.end())
        return nullptr;

    // Create the used-chunk, taking its space from the end of the free-chunk
    const size_t size_remaining = size_ptr_it->first - size;
    auto alloced = chunks_used.emplace(size_ptr_it->second + size_remaining, size).first;
    chunks_free_end.erase(size_ptr_it->second + size_ptr_it->first);
    if (size_ptr_it->first == size) {
        // whole chunk is used up
        chunks_free.erase(size_ptr_it->second);
    } else {
        // still some memory left in the chunk
        auto it_remaining = size_to_free_chunk.emplace(size_remaining, size_ptr_it->second);
        chunks_free[size_ptr_it->second] = it_remaining;
        chunks_free_end.emplace(size_ptr_it->second + size_remaining, it_remaining);
    }
    size_to_free_chunk.erase(size_ptr_it);

    return reinterpret_cast<void*>(alloced->first);
}

void Arena::free(void *ptr)
{
    // Freeing the nullptr pointer is OK.
    if (ptr == nullptr) {
        return;
    }

    // Remove chunk from used map
    auto i = chunks_used.find(static_cast<char*>(ptr));
    if (i == chunks_used.end()) {
        throw std::runtime_error("Arena: invalid or double free");
    }
    std::pair<char*, size_t> freed = *i;
    chunks_used.erase(i);

    // coalesce freed with previous chunk
    auto prev = chunks_free_end.find(freed.first);
    if (prev != chunks_free_end.end()) {
        freed.first -= prev->second->first;
        freed.second += prev->second->first;
        size_to_free_chunk.erase(prev->second);
        chunks_free_end.erase(prev);
    }

    // coalesce freed with chunk after freed
    auto next = chunks_free.find(freed.first + freed.second);
    if (next != chunks_free.end()) {
        freed.second += next->second->first;
        size_to_free_chunk.erase(next->second);
        chunks_free.erase(next);
    }

    // Add/set space with coalesced free chunk
    auto it = size_to_free_chunk.emplace(freed.second, freed.first);
    chunks_free[freed.first] = it;
    chunks_free_end[freed.first + freed.second] = it;
}

Arena::Stats Arena::stats() const
{
    Arena::Stats r{ 0, 0, 0, chunks_used.size(), chunks_free.size() };
    for (const auto& chunk: chunks_used)
        r.used += chunk.second;
    for (const auto& chunk: chunks_free)
        r.free += chunk.second->first;
    r.total = r.used + r.free;
    return r;
}

#ifdef ARENA_DEBUG
static void printchunk(char* base, size_t sz, bool used) {
    std::cout <<
        "0x" << std::hex << std::setw(16) << std::setfill('0') << base <<
        " 0x" << std::hex << std::setw(16) << std::setfill('0') << sz <<
        " 0x" << used << std::endl;
}
void Arena::walk() const
{
    for (const auto& chunk: chunks_used)
        printchunk(chunk.first, chunk.second, true);
    std::cout << std::endl;
    for (const auto& chunk: chunks_free)
        printchunk(chunk.first, chunk.second, false);
    std::cout << std::endl;
}
#endif

/*******************************************************************************/
// Implementation: Win32LockedPageAllocator

#ifdef WIN32
/** LockedPageAllocator specialized for Windows.
 */
class Win32LockedPageAllocator: public LockedPageAllocator
{
public:
    Win32LockedPageAllocator();
    void* AllocateLocked(size_t len, bool *lockingSuccess) override;
    void FreeLocked(void* addr, size_t len) override;
    size_t GetLimit() override;
private:
    size_t page_size;
};

Win32LockedPageAllocator::Win32LockedPageAllocator()
{
    // Determine system page size in bytes
    SYSTEM_INFO sSysInfo;
    GetSystemInfo(&sSysInfo);
    page_size = sSysInfo.dwPageSize;
}
void *Win32LockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
{
    len = align_up(len, page_size);
    void *addr = VirtualAlloc(nullptr, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (addr) {
        // VirtualLock is used to attempt to keep keying material out of swap. Note
        // that it does not provide this as a guarantee, but, in practice, memory
        // that has been VirtualLock'd almost never gets written to the pagefile
        // except in rare circumstances where memory is extremely low.
        *lockingSuccess = VirtualLock(const_cast<void*>(addr), len) != 0;
    }
    return addr;
}
void Win32LockedPageAllocator::FreeLocked(void* addr, size_t len)
{
    len = align_up(len, page_size);
    memory_cleanse(addr, len);
    VirtualUnlock(const_cast<void*>(addr), len);
}

size_t Win32LockedPageAllocator::GetLimit()
{
    // TODO is there a limit on Windows, how to get it?
    return std::numeric_limits<size_t>::max();
}
#endif

/*******************************************************************************/
// Implementation: PosixLockedPageAllocator

#ifndef WIN32
/** LockedPageAllocator specialized for OSes that don't try to be
 * special snowflakes.
 */
class PosixLockedPageAllocator: public LockedPageAllocator
{
public:
    PosixLockedPageAllocator();
    void* AllocateLocked(size_t len, bool *lockingSuccess) override;
    void FreeLocked(void* addr, size_t len) override;
    size_t GetLimit() override;
private:
    size_t page_size;
};

PosixLockedPageAllocator::PosixLockedPageAllocator()
{
    // Determine system page size in bytes
#if defined(PAGESIZE) // defined in limits.h
    page_size = PAGESIZE;
#else                   // assume some POSIX OS
    page_size = sysconf(_SC_PAGESIZE);
#endif
}

// Some systems (at least OS X) do not define MAP_ANONYMOUS yet and define
// MAP_ANON which is deprecated
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif

void *PosixLockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
{
    void *addr;
    len = align_up(len, page_size);
    addr = mmap(nullptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    if (addr) {
        *lockingSuccess = mlock(addr, len) == 0;
    }
    return addr;
}
void PosixLockedPageAllocator::FreeLocked(void* addr, size_t len)
{
    len = align_up(len, page_size);
    memory_cleanse(addr, len);
    munlock(addr, len);
    munmap(addr, len);
}
size_t PosixLockedPageAllocator::GetLimit()
{
#ifdef RLIMIT_MEMLOCK
    struct rlimit rlim;
    if (getrlimit(RLIMIT_MEMLOCK, &rlim) == 0) {
        if (rlim.rlim_cur != RLIM_INFINITY) {
            return rlim.rlim_cur;
        }
    }
#endif
    return std::numeric_limits<size_t>::max();
}
#endif

/*******************************************************************************/
// Implementation: LockedPool

LockedPool::LockedPool(std::unique_ptr<LockedPageAllocator> allocator_in, LockingFailed_Callback lf_cb_in):
    allocator(std::move(allocator_in)), lf_cb(lf_cb_in), cumulative_bytes_locked(0)
{
}

LockedPool::~LockedPool()
{
}
void* LockedPool::alloc(size_t size)
{
    std::lock_guard<std::mutex> lock(mutex);

    // Don't handle impossible sizes
    if (size == 0 || size > ARENA_SIZE)
        return nullptr;

    // Try allocating from each current arena
    for (auto &arena: arenas) {
        void *addr = arena.alloc(size);
        if (addr) {
            return addr;
        }
    }
    // If that fails, create a new one
    if (new_arena(ARENA_SIZE, ARENA_ALIGN)) {
        return arenas.back().alloc(size);
    }
    return nullptr;
}

void LockedPool::free(void *ptr)
{
    std::lock_guard<std::mutex> lock(mutex);
    // TODO we can do better than this linear search by keeping a map of arena
    // extents to arena, and looking up the address.
    for (auto &arena: arenas) {
        if (arena.addressInArena(ptr)) {
            arena.free(ptr);
            return;
        }
    }
    throw std::runtime_error("LockedPool: invalid address not pointing to any arena");
}

LockedPool::Stats LockedPool::stats() const
{
    std::lock_guard<std::mutex> lock(mutex);
    LockedPool::Stats r{0, 0, 0, cumulative_bytes_locked, 0, 0};
    for (const auto &arena: arenas) {
        Arena::Stats i = arena.stats();
        r.used += i.used;
        r.free += i.free;
        r.total += i.total;
        r.chunks_used += i.chunks_used;
        r.chunks_free += i.chunks_free;
    }
    return r;
}

bool LockedPool::new_arena(size_t size, size_t align)
{
    bool locked;
    // If this is the first arena, handle this specially: Cap the upper size
    // by the process limit. This makes sure that the first arena will at least
    // be locked. An exception to this is if the process limit is 0:
    // in this case no memory can be locked at all so we'll skip past this logic.
    if (arenas.empty()) {
        size_t limit = allocator->GetLimit();
        if (limit > 0) {
            size = std::min(size, limit);
        }
    }
    void *addr = allocator->AllocateLocked(size, &locked);
    if (!addr) {
        return false;
    }
    if (locked) {
        cumulative_bytes_locked += size;
    } else if (lf_cb) { // Call the locking-failed callback if locking failed
        if (!lf_cb()) { // If the callback returns false, free the memory and fail, otherwise consider the user warned and proceed.
            allocator->FreeLocked(addr, size);
            return false;
        }
    }
    arenas.emplace_back(allocator.get(), addr, size, align);
    return true;
}

LockedPool::LockedPageArena::LockedPageArena(LockedPageAllocator *allocator_in, void *base_in, size_t size_in, size_t align_in):
    Arena(base_in, size_in, align_in), base(base_in), size(size_in), allocator(allocator_in)
{
}
LockedPool::LockedPageArena::~LockedPageArena()
{
    allocator->FreeLocked(base, size);
}

/*******************************************************************************/
// Implementation: LockedPoolManager
//
LockedPoolManager::LockedPoolManager(std::unique_ptr<LockedPageAllocator> allocator_in):
    LockedPool(std::move(allocator_in), &LockedPoolManager::LockingFailed)
{
}

bool LockedPoolManager::LockingFailed()
{
    // TODO: log something but how? without including util.h
    return true;
}

void LockedPoolManager::CreateInstance()
{
    // Using a local static instance guarantees that the object is initialized
    // when it's first needed and also deinitialized after all objects that use
    // it are done with it.  I can think of one unlikely scenario where we may
    // have a static deinitialization order/problem, but the check in
    // LockedPoolManagerBase's destructor helps us detect if that ever happens.
#ifdef WIN32
    std::unique_ptr<LockedPageAllocator> allocator(new Win32LockedPageAllocator());
#else
    std::unique_ptr<LockedPageAllocator> allocator(new PosixLockedPageAllocator());
#endif
    static LockedPoolManager instance(std::move(allocator));
    LockedPoolManager::_instance = &instance;
}
<commit_msg>lockedpool: When possible, use madvise to avoid including sensitive information in core dumps<commit_after>// Copyright (c) 2016-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <support/lockedpool.h>
#include <support/cleanse.h>

#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif

#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <sys/mman.h> // for mmap
#include <sys/resource.h> // for getrlimit
#include <limits.h> // for PAGESIZE
#include <unistd.h> // for sysconf
#endif

#include <algorithm>

LockedPoolManager* LockedPoolManager::_instance = nullptr;
std::once_flag LockedPoolManager::init_flag;

/*******************************************************************************/
// Utilities
//
/** Align up to power of 2 */
static inline size_t align_up(size_t x, size_t align)
{
    return (x + align - 1) & ~(align - 1);
}

/*******************************************************************************/
// Implementation: Arena

Arena::Arena(void *base_in, size_t size_in, size_t alignment_in):
    base(static_cast<char*>(base_in)), end(static_cast<char*>(base_in) + size_in), alignment(alignment_in)
{
    // Start with one free chunk that covers the entire arena
    auto it = size_to_free_chunk.emplace(size_in, base);
    chunks_free.emplace(base, it);
    chunks_free_end.emplace(base + size_in, it);
}

Arena::~Arena()
{
}

void* Arena::alloc(size_t size)
{
    // Round to next multiple of alignment
    size = align_up(size, alignment);

    // Don't handle zero-sized chunks
    if (size == 0)
        return nullptr;

    // Pick a large enough free-chunk. Returns an iterator pointing to the first element that is not less than key.
    // This allocation strategy is best-fit. According to "Dynamic Storage Allocation: A Survey and Critical Review",
    // Wilson et. al. 1995, http://www.scs.stanford.edu/14wi-cs140/sched/readings/wilson.pdf, best-fit and first-fit
    // policies seem to work well in practice.
    auto size_ptr_it = size_to_free_chunk.lower_bound(size);
    if (size_ptr_it == size_to_free_chunk.end())
        return nullptr;

    // Create the used-chunk, taking its space from the end of the free-chunk
    const size_t size_remaining = size_ptr_it->first - size;
    auto alloced = chunks_used.emplace(size_ptr_it->second + size_remaining, size).first;
    chunks_free_end.erase(size_ptr_it->second + size_ptr_it->first);
    if (size_ptr_it->first == size) {
        // whole chunk is used up
        chunks_free.erase(size_ptr_it->second);
    } else {
        // still some memory left in the chunk
        auto it_remaining = size_to_free_chunk.emplace(size_remaining, size_ptr_it->second);
        chunks_free[size_ptr_it->second] = it_remaining;
        chunks_free_end.emplace(size_ptr_it->second + size_remaining, it_remaining);
    }
    size_to_free_chunk.erase(size_ptr_it);

    return reinterpret_cast<void*>(alloced->first);
}

void Arena::free(void *ptr)
{
    // Freeing the nullptr pointer is OK.
    if (ptr == nullptr) {
        return;
    }

    // Remove chunk from used map
    auto i = chunks_used.find(static_cast<char*>(ptr));
    if (i == chunks_used.end()) {
        throw std::runtime_error("Arena: invalid or double free");
    }
    std::pair<char*, size_t> freed = *i;
    chunks_used.erase(i);

    // coalesce freed with previous chunk
    auto prev = chunks_free_end.find(freed.first);
    if (prev != chunks_free_end.end()) {
        freed.first -= prev->second->first;
        freed.second += prev->second->first;
        size_to_free_chunk.erase(prev->second);
        chunks_free_end.erase(prev);
    }

    // coalesce freed with chunk after freed
    auto next = chunks_free.find(freed.first + freed.second);
    if (next != chunks_free.end()) {
        freed.second += next->second->first;
        size_to_free_chunk.erase(next->second);
        chunks_free.erase(next);
    }

    // Add/set space with coalesced free chunk
    auto it = size_to_free_chunk.emplace(freed.second, freed.first);
    chunks_free[freed.first] = it;
    chunks_free_end[freed.first + freed.second] = it;
}

Arena::Stats Arena::stats() const
{
    Arena::Stats r{ 0, 0, 0, chunks_used.size(), chunks_free.size() };
    for (const auto& chunk: chunks_used)
        r.used += chunk.second;
    for (const auto& chunk: chunks_free)
        r.free += chunk.second->first;
    r.total = r.used + r.free;
    return r;
}

#ifdef ARENA_DEBUG
static void printchunk(char* base, size_t sz, bool used) {
    std::cout <<
        "0x" << std::hex << std::setw(16) << std::setfill('0') << base <<
        " 0x" << std::hex << std::setw(16) << std::setfill('0') << sz <<
        " 0x" << used << std::endl;
}
void Arena::walk() const
{
    for (const auto& chunk: chunks_used)
        printchunk(chunk.first, chunk.second, true);
    std::cout << std::endl;
    for (const auto& chunk: chunks_free)
        printchunk(chunk.first, chunk.second, false);
    std::cout << std::endl;
}
#endif

/*******************************************************************************/
// Implementation: Win32LockedPageAllocator

#ifdef WIN32
/** LockedPageAllocator specialized for Windows.
 */
class Win32LockedPageAllocator: public LockedPageAllocator
{
public:
    Win32LockedPageAllocator();
    void* AllocateLocked(size_t len, bool *lockingSuccess) override;
    void FreeLocked(void* addr, size_t len) override;
    size_t GetLimit() override;
private:
    size_t page_size;
};

Win32LockedPageAllocator::Win32LockedPageAllocator()
{
    // Determine system page size in bytes
    SYSTEM_INFO sSysInfo;
    GetSystemInfo(&sSysInfo);
    page_size = sSysInfo.dwPageSize;
}
void *Win32LockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
{
    len = align_up(len, page_size);
    void *addr = VirtualAlloc(nullptr, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (addr) {
        // VirtualLock is used to attempt to keep keying material out of swap. Note
        // that it does not provide this as a guarantee, but, in practice, memory
        // that has been VirtualLock'd almost never gets written to the pagefile
        // except in rare circumstances where memory is extremely low.
        *lockingSuccess = VirtualLock(const_cast<void*>(addr), len) != 0;
    }
    return addr;
}
void Win32LockedPageAllocator::FreeLocked(void* addr, size_t len)
{
    len = align_up(len, page_size);
    memory_cleanse(addr, len);
    VirtualUnlock(const_cast<void*>(addr), len);
}

size_t Win32LockedPageAllocator::GetLimit()
{
    // TODO is there a limit on Windows, how to get it?
    return std::numeric_limits<size_t>::max();
}
#endif

/*******************************************************************************/
// Implementation: PosixLockedPageAllocator

#ifndef WIN32
/** LockedPageAllocator specialized for OSes that don't try to be
 * special snowflakes.
 */
class PosixLockedPageAllocator: public LockedPageAllocator
{
public:
    PosixLockedPageAllocator();
    void* AllocateLocked(size_t len, bool *lockingSuccess) override;
    void FreeLocked(void* addr, size_t len) override;
    size_t GetLimit() override;
private:
    size_t page_size;
};

PosixLockedPageAllocator::PosixLockedPageAllocator()
{
    // Determine system page size in bytes
#if defined(PAGESIZE) // defined in limits.h
    page_size = PAGESIZE;
#else                   // assume some POSIX OS
    page_size = sysconf(_SC_PAGESIZE);
#endif
}

// Some systems (at least OS X) do not define MAP_ANONYMOUS yet and define
// MAP_ANON which is deprecated
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif

void *PosixLockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
{
    void *addr;
    len = align_up(len, page_size);
    addr = mmap(nullptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    if (addr) {
        *lockingSuccess = mlock(addr, len) == 0;
#ifdef MADV_DONTDUMP
        madvise(addr, len, MADV_DONTDUMP);
#endif
    }
    return addr;
}
void PosixLockedPageAllocator::FreeLocked(void* addr, size_t len)
{
    len = align_up(len, page_size);
    memory_cleanse(addr, len);
    munlock(addr, len);
    munmap(addr, len);
}
size_t PosixLockedPageAllocator::GetLimit()
{
#ifdef RLIMIT_MEMLOCK
    struct rlimit rlim;
    if (getrlimit(RLIMIT_MEMLOCK, &rlim) == 0) {
        if (rlim.rlim_cur != RLIM_INFINITY) {
            return rlim.rlim_cur;
        }
    }
#endif
    return std::numeric_limits<size_t>::max();
}
#endif

/*******************************************************************************/
// Implementation: LockedPool

LockedPool::LockedPool(std::unique_ptr<LockedPageAllocator> allocator_in, LockingFailed_Callback lf_cb_in):
    allocator(std::move(allocator_in)), lf_cb(lf_cb_in), cumulative_bytes_locked(0)
{
}

LockedPool::~LockedPool()
{
}
void* LockedPool::alloc(size_t size)
{
    std::lock_guard<std::mutex> lock(mutex);

    // Don't handle impossible sizes
    if (size == 0 || size > ARENA_SIZE)
        return nullptr;

    // Try allocating from each current arena
    for (auto &arena: arenas) {
        void *addr = arena.alloc(size);
        if (addr) {
            return addr;
        }
    }
    // If that fails, create a new one
    if (new_arena(ARENA_SIZE, ARENA_ALIGN)) {
        return arenas.back().alloc(size);
    }
    return nullptr;
}

void LockedPool::free(void *ptr)
{
    std::lock_guard<std::mutex> lock(mutex);
    // TODO we can do better than this linear search by keeping a map of arena
    // extents to arena, and looking up the address.
    for (auto &arena: arenas) {
        if (arena.addressInArena(ptr)) {
            arena.free(ptr);
            return;
        }
    }
    throw std::runtime_error("LockedPool: invalid address not pointing to any arena");
}

LockedPool::Stats LockedPool::stats() const
{
    std::lock_guard<std::mutex> lock(mutex);
    LockedPool::Stats r{0, 0, 0, cumulative_bytes_locked, 0, 0};
    for (const auto &arena: arenas) {
        Arena::Stats i = arena.stats();
        r.used += i.used;
        r.free += i.free;
        r.total += i.total;
        r.chunks_used += i.chunks_used;
        r.chunks_free += i.chunks_free;
    }
    return r;
}

bool LockedPool::new_arena(size_t size, size_t align)
{
    bool locked;
    // If this is the first arena, handle this specially: Cap the upper size
    // by the process limit. This makes sure that the first arena will at least
    // be locked. An exception to this is if the process limit is 0:
    // in this case no memory can be locked at all so we'll skip past this logic.
    if (arenas.empty()) {
        size_t limit = allocator->GetLimit();
        if (limit > 0) {
            size = std::min(size, limit);
        }
    }
    void *addr = allocator->AllocateLocked(size, &locked);
    if (!addr) {
        return false;
    }
    if (locked) {
        cumulative_bytes_locked += size;
    } else if (lf_cb) { // Call the locking-failed callback if locking failed
        if (!lf_cb()) { // If the callback returns false, free the memory and fail, otherwise consider the user warned and proceed.
            allocator->FreeLocked(addr, size);
            return false;
        }
    }
    arenas.emplace_back(allocator.get(), addr, size, align);
    return true;
}

LockedPool::LockedPageArena::LockedPageArena(LockedPageAllocator *allocator_in, void *base_in, size_t size_in, size_t align_in):
    Arena(base_in, size_in, align_in), base(base_in), size(size_in), allocator(allocator_in)
{
}
LockedPool::LockedPageArena::~LockedPageArena()
{
    allocator->FreeLocked(base, size);
}

/*******************************************************************************/
// Implementation: LockedPoolManager
//
LockedPoolManager::LockedPoolManager(std::unique_ptr<LockedPageAllocator> allocator_in):
    LockedPool(std::move(allocator_in), &LockedPoolManager::LockingFailed)
{
}

bool LockedPoolManager::LockingFailed()
{
    // TODO: log something but how? without including util.h
    return true;
}

void LockedPoolManager::CreateInstance()
{
    // Using a local static instance guarantees that the object is initialized
    // when it's first needed and also deinitialized after all objects that use
    // it are done with it.  I can think of one unlikely scenario where we may
    // have a static deinitialization order/problem, but the check in
    // LockedPoolManagerBase's destructor helps us detect if that ever happens.
#ifdef WIN32
    std::unique_ptr<LockedPageAllocator> allocator(new Win32LockedPageAllocator());
#else
    std::unique_ptr<LockedPageAllocator> allocator(new PosixLockedPageAllocator());
#endif
    static LockedPoolManager instance(std::move(allocator));
    LockedPoolManager::_instance = &instance;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved.
 */

#include "base/os.h"
#include "testing/gunit.h"

#include <base/logging.h>
#include <io/event_manager.h>
#include <io/test/event_manager_test.h>
#include <tbb/task.h>
#include <base/task.h>

#include <cmn/agent_cmn.h>

#include "cfg/cfg_init.h"
#include "cfg/cfg_interface.h"
#include "pkt/pkt_init.h"
#include "services/services_init.h"
#include "vrouter/ksync/ksync_init.h"
#include "oper/interface_common.h"
#include "oper/nexthop.h"
#include "oper/tunnel_nh.h"
#include "route/route.h"
#include "oper/vrf.h"
#include "oper/mpls.h"
#include "oper/vm.h"
#include "oper/vn.h"
#include "oper/physical_device_vn.h"
#include "filter/acl.h"
#include "openstack/instance_service_server.h"
#include "test_cmn_util.h"
#include "vr_types.h"

#include "openstack/instance_service_server.h"
#include "xmpp/xmpp_init.h"
#include "xmpp/test/xmpp_test_util.h"
#include "vr_types.h"
#include "control_node_mock.h"
#include "xml/xml_pugi.h"
#include "controller/controller_peer.h"
#include "controller/controller_export.h"
#include "controller/controller_vrf_export.h"

#include "ovs_tor_agent/ovsdb_client/ovsdb_route_peer.h"
#include "ovs_tor_agent/ovsdb_client/physical_switch_ovsdb.h"
#include "ovs_tor_agent/ovsdb_client/logical_switch_ovsdb.h"
#include "ovs_tor_agent/ovsdb_client/physical_port_ovsdb.h"
#include "test_ovs_agent_init.h"
#include "test-xml/test_xml.h"
#include "test-xml/test_xml_oper.h"
#include "test_xml_physical_device.h"
#include "test_xml_ovsdb.h"

#include <ovsdb_types.h>

using namespace pugi;
using namespace OVSDB;

EventManager evm1;
ServerThread *thread1;
test::ControlNodeMock *bgp_peer1;

EventManager evm2;
ServerThread *thread2;
test::ControlNodeMock *bgp_peer2;

void RouterIdDepInit(Agent *agent) {
    Agent::GetInstance()->controller()->Connect();
}

class OvsBaseTest : public ::testing::Test {
protected:
    OvsBaseTest() {
    }

    virtual void SetUp() {
        agent_ = Agent::GetInstance();
        init_ = static_cast<TestOvsAgentInit *>(client->agent_init());
        peer_manager_ = init_->ovs_peer_manager();
        WAIT_FOR(100, 10000,
                 (tcp_session_ = static_cast<OvsdbClientTcpSession *>
                  (init_->ovsdb_client()->NextSession(NULL))) != NULL);
        WAIT_FOR(100, 10000,
                 (tcp_session_->client_idl() != NULL));
    }

    virtual void TearDown() {
    }

    void AddPhysicalDeviceVn(int dev_id, int vn_id) {
        DBRequest req(DBRequest::DB_ENTRY_ADD_CHANGE);
        req.key.reset(new PhysicalDeviceVnKey(MakeUuid(dev_id),
                                              MakeUuid(vn_id)));
        agent_->physical_device_vn_table()->Enqueue(&req);
        PhysicalDeviceVn key(MakeUuid(dev_id), MakeUuid(vn_id));
        WAIT_FOR(100, 10000,
               (agent_->physical_device_vn_table()->Find(&key, false) != NULL));
    }

    void DelPhysicalDeviceVn(int dev_id, int vn_id) {
        DBRequest req(DBRequest::DB_ENTRY_DELETE);
        req.key.reset(new PhysicalDeviceVnKey(MakeUuid(dev_id),
                                              MakeUuid(vn_id)));
        agent_->physical_device_vn_table()->Enqueue(&req);
        PhysicalDeviceVn key(MakeUuid(dev_id), MakeUuid(vn_id));
        WAIT_FOR(100, 10000,
                (agent_->physical_device_vn_table()->Find(&key, false) == NULL));
    }

    Agent *agent_;
    TestOvsAgentInit *init_;
    OvsPeerManager *peer_manager_;
    OvsdbClientTcpSession *tcp_session_;
};

TEST_F(OvsBaseTest, BasicOvsdb) {
    WAIT_FOR(100, 10000, (tcp_session_->status() == string("Established")));
}

TEST_F(OvsBaseTest, MulticastLocalBasic) {
    AgentUtXmlTest
        test("controller/src/vnsw/agent/ovs_tor_agent/ovsdb_client/test/xml/multicast-local-base.xml");
    // set current session in test context
    OvsdbTestSetSessionContext(tcp_session_);
    AgentUtXmlOperInit(&test);
    AgentUtXmlPhysicalDeviceInit(&test);
    AgentUtXmlOvsdbInit(&test);
    if (test.Load() == true) {
        test.ReadXml();
        string str;
        test.ToString(&str);
        cout << str << endl;
        test.Run();
    }
}

TEST_F(OvsBaseTest, MulticastLocal_add_mcroute_without_vrf_vn_link_present) {
    //Add vrf
    VrfAddReq("vrf1");
    WAIT_FOR(100, 10000, (VrfGet("vrf1", false) != NULL));
    //Add VN
    VnAddReq(1, "vn1", "vrf1");
    WAIT_FOR(100, 10000, (VnGet(1) != NULL));
    //Add device
    DBRequest device_req(DBRequest::DB_ENTRY_ADD_CHANGE);
    device_req.key.reset(new PhysicalDeviceKey(MakeUuid(1)));
    device_req.data.reset(new PhysicalDeviceData(agent_, "test-router",
                                                 "test-router", "",
                                                 Ip4Address::from_string("1.1.1.1"),
                                                 Ip4Address::from_string("2.2.2.2"),
                                                 "OVS", NULL));
    agent_->physical_device_table()->Enqueue(&device_req);
    WAIT_FOR(100, 10000,
             (agent_->physical_device_table()->Find(MakeUuid(1)) != NULL));
    //Add device_vn
    AddPhysicalDeviceVn(1, 1);

    //Initialization done, now delete VRF VN link and then update VXLAN id in
    //VN.
    TestClient::WaitForIdle();
    VxLanNetworkIdentifierMode(true);
    TestClient::WaitForIdle();

    //Delete
    DelPhysicalDeviceVn(1, 1);
    DBRequest del_dev_req(DBRequest::DB_ENTRY_DELETE);
    del_dev_req.key.reset(new PhysicalDeviceVnKey(MakeUuid(1),
                                                  MakeUuid(1)));
    agent_->physical_device_table()->Enqueue(&del_dev_req);
    WAIT_FOR(1000, 10000,
             (agent_->physical_device_table()->
              Find(MakeUuid(1)) == NULL));
    VrfDelReq("vrf1");
    VnDelReq(1);
    WAIT_FOR(1000, 10000, (VrfGet("vrf1", true) != NULL));
    WAIT_FOR(1000, 10000, (VnGet(1) != NULL));
}

int main(int argc, char *argv[]) {
    GETUSERARGS();
    // override with true to initialize ovsdb server and client
    ksync_init = true;
    client = OvsTestInit(init_file, ksync_init);
    int ret = RUN_ALL_TESTS();
    TestShutdown();
    return ret;
}
<commit_msg>Wrong check for VN and vrf delete in test case.<commit_after>/*
 * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved.
 */

#include "base/os.h"
#include "testing/gunit.h"

#include <base/logging.h>
#include <io/event_manager.h>
#include <io/test/event_manager_test.h>
#include <tbb/task.h>
#include <base/task.h>

#include <cmn/agent_cmn.h>

#include "cfg/cfg_init.h"
#include "cfg/cfg_interface.h"
#include "pkt/pkt_init.h"
#include "services/services_init.h"
#include "vrouter/ksync/ksync_init.h"
#include "oper/interface_common.h"
#include "oper/nexthop.h"
#include "oper/tunnel_nh.h"
#include "route/route.h"
#include "oper/vrf.h"
#include "oper/mpls.h"
#include "oper/vm.h"
#include "oper/vn.h"
#include "oper/physical_device_vn.h"
#include "filter/acl.h"
#include "openstack/instance_service_server.h"
#include "test_cmn_util.h"
#include "vr_types.h"

#include "openstack/instance_service_server.h"
#include "xmpp/xmpp_init.h"
#include "xmpp/test/xmpp_test_util.h"
#include "vr_types.h"
#include "control_node_mock.h"
#include "xml/xml_pugi.h"
#include "controller/controller_peer.h"
#include "controller/controller_export.h"
#include "controller/controller_vrf_export.h"

#include "ovs_tor_agent/ovsdb_client/ovsdb_route_peer.h"
#include "ovs_tor_agent/ovsdb_client/physical_switch_ovsdb.h"
#include "ovs_tor_agent/ovsdb_client/logical_switch_ovsdb.h"
#include "ovs_tor_agent/ovsdb_client/physical_port_ovsdb.h"
#include "test_ovs_agent_init.h"
#include "test-xml/test_xml.h"
#include "test-xml/test_xml_oper.h"
#include "test_xml_physical_device.h"
#include "test_xml_ovsdb.h"

#include <ovsdb_types.h>

using namespace pugi;
using namespace OVSDB;

EventManager evm1;
ServerThread *thread1;
test::ControlNodeMock *bgp_peer1;

EventManager evm2;
ServerThread *thread2;
test::ControlNodeMock *bgp_peer2;

void RouterIdDepInit(Agent *agent) {
    Agent::GetInstance()->controller()->Connect();
}

class OvsBaseTest : public ::testing::Test {
protected:
    OvsBaseTest() {
    }

    virtual void SetUp() {
        agent_ = Agent::GetInstance();
        init_ = static_cast<TestOvsAgentInit *>(client->agent_init());
        peer_manager_ = init_->ovs_peer_manager();
        WAIT_FOR(100, 10000,
                 (tcp_session_ = static_cast<OvsdbClientTcpSession *>
                  (init_->ovsdb_client()->NextSession(NULL))) != NULL);
        WAIT_FOR(100, 10000,
                 (tcp_session_->client_idl() != NULL));
    }

    virtual void TearDown() {
    }

    void AddPhysicalDeviceVn(int dev_id, int vn_id) {
        DBRequest req(DBRequest::DB_ENTRY_ADD_CHANGE);
        req.key.reset(new PhysicalDeviceVnKey(MakeUuid(dev_id),
                                              MakeUuid(vn_id)));
        agent_->physical_device_vn_table()->Enqueue(&req);
        PhysicalDeviceVn key(MakeUuid(dev_id), MakeUuid(vn_id));
        WAIT_FOR(100, 10000,
               (agent_->physical_device_vn_table()->Find(&key, false) != NULL));
    }

    void DelPhysicalDeviceVn(int dev_id, int vn_id) {
        DBRequest req(DBRequest::DB_ENTRY_DELETE);
        req.key.reset(new PhysicalDeviceVnKey(MakeUuid(dev_id),
                                              MakeUuid(vn_id)));
        agent_->physical_device_vn_table()->Enqueue(&req);
        PhysicalDeviceVn key(MakeUuid(dev_id), MakeUuid(vn_id));
        WAIT_FOR(100, 10000,
                (agent_->physical_device_vn_table()->Find(&key, false) == NULL));
    }

    Agent *agent_;
    TestOvsAgentInit *init_;
    OvsPeerManager *peer_manager_;
    OvsdbClientTcpSession *tcp_session_;
};

TEST_F(OvsBaseTest, BasicOvsdb) {
    WAIT_FOR(100, 10000, (tcp_session_->status() == string("Established")));
}

TEST_F(OvsBaseTest, MulticastLocalBasic) {
    AgentUtXmlTest
        test("controller/src/vnsw/agent/ovs_tor_agent/ovsdb_client/test/xml/multicast-local-base.xml");
    // set current session in test context
    OvsdbTestSetSessionContext(tcp_session_);
    AgentUtXmlOperInit(&test);
    AgentUtXmlPhysicalDeviceInit(&test);
    AgentUtXmlOvsdbInit(&test);
    if (test.Load() == true) {
        test.ReadXml();
        string str;
        test.ToString(&str);
        cout << str << endl;
        test.Run();
    }
}

TEST_F(OvsBaseTest, MulticastLocal_add_mcroute_without_vrf_vn_link_present) {
    //Add vrf
    VrfAddReq("vrf1");
    WAIT_FOR(100, 10000, (VrfGet("vrf1", false) != NULL));
    //Add VN
    VnAddReq(1, "vn1", "vrf1");
    WAIT_FOR(100, 10000, (VnGet(1) != NULL));
    //Add device
    DBRequest device_req(DBRequest::DB_ENTRY_ADD_CHANGE);
    device_req.key.reset(new PhysicalDeviceKey(MakeUuid(1)));
    device_req.data.reset(new PhysicalDeviceData(agent_, "test-router",
                                                 "test-router", "",
                                                 Ip4Address::from_string("1.1.1.1"),
                                                 Ip4Address::from_string("2.2.2.2"),
                                                 "OVS", NULL));
    agent_->physical_device_table()->Enqueue(&device_req);
    WAIT_FOR(100, 10000,
             (agent_->physical_device_table()->Find(MakeUuid(1)) != NULL));
    //Add device_vn
    AddPhysicalDeviceVn(1, 1);

    //Initialization done, now delete VRF VN link and then update VXLAN id in
    //VN.
    TestClient::WaitForIdle();
    VxLanNetworkIdentifierMode(true);
    TestClient::WaitForIdle();

    //Delete
    DelPhysicalDeviceVn(1, 1);
    DBRequest del_dev_req(DBRequest::DB_ENTRY_DELETE);
    del_dev_req.key.reset(new PhysicalDeviceVnKey(MakeUuid(1),
                                                  MakeUuid(1)));
    agent_->physical_device_table()->Enqueue(&del_dev_req);
    WAIT_FOR(1000, 10000,
             (agent_->physical_device_table()->
              Find(MakeUuid(1)) == NULL));
    VrfDelReq("vrf1");
    VnDelReq(1);
    WAIT_FOR(1000, 10000, (VrfGet("vrf1", true) == NULL));
    WAIT_FOR(1000, 10000, (VnGet(1) == NULL));
}

int main(int argc, char *argv[]) {
    GETUSERARGS();
    // override with true to initialize ovsdb server and client
    ksync_init = true;
    client = OvsTestInit(init_file, ksync_init);
    int ret = RUN_ALL_TESTS();
    TestShutdown();
    return ret;
}
<|endoftext|>


<commit_before>#include "augs/templates/for_each_std_get.h"

#include "augs/readwrite/memory_stream.h"

#include "augs/misc/imgui/imgui_utils.h"
#include "augs/misc/imgui/imgui_scope_wrappers.h"
#include "augs/misc/imgui/imgui_control_wrappers.h"

#include "application/intercosm.h"
#include "application/setups/editor/editor_folder.h"
#include "application/setups/editor/gui/editor_all_entities_gui.h"

#include "application/setups/editor/gui/editor_properties_gui.h"

#include "augs/readwrite/memory_stream.h"
#include "augs/readwrite/byte_readwrite.h"

using namespace augs::imgui;

static auto details_text = [](const std::string& tx = ""){
	ImGui::NextColumn();

	if (tx.size() > 0) {
		text(tx);
	}

	ImGui::NextColumn();
};

template <class T>
auto get_invariant_stem(const T&) {
	auto result = format_field_name(get_type_name_strip_namespace<T>());
	result[0] = std::toupper(result[0]);

	/* These two look ugly with automated names */

	if constexpr(std::is_same_v<T, invariants::sprite>) {
		result = "Sprite";
	}

	if constexpr(std::is_same_v<T, invariants::polygon>) {
		result = "Polygon";
	}

	return result;
}

template <class T>
decltype(auto) get_name_of(const entity_flavour<T>& flavour) {
	return flavour.template get<invariants::name>().name;
}

template <class T, class E>
void edit_invariant(
	const T& invariant,
	const entity_flavour<E>& source_flavour,
	const typed_entity_flavour_id<E> flavour_id,
   	const editor_command_input in
) {
	thread_local std::optional<ImGuiID> last_active;

	auto& history = in.folder.history;

	auto current_offset = static_cast<unsigned>(-1);
	auto current_type_id = edited_field_type_id();

	auto make_property_id = [&](){
		flavour_property_id result;
		result.field_offset = current_offset;
		result.field_type = current_type_id;
		result.invariant_id = index_in_list_v<T, decltype(entity_flavour<E>::invariants)>;
		result.flavour_id = flavour_id;

		return result;
	};

	const auto describe_property_location = [&](){
		const auto flavour_name = get_name_of(source_flavour);
		const auto invariant_name = get_invariant_stem(invariant);

		return typesafe_sprintf("(in %x of %x)", invariant_name, flavour_name);
	};

	/*
		Change description format:

		[address] = Muzzle velocity (in Gun of Amplifier arm)

		Bools:

		Set [address]
		Unset [address]

		Other fields:

		Set [address] to [value]

		Name:

		Renamed [old name] to [new name]
	*/

	auto describe_changed_flag = [&](
		const auto& field_name,
		const auto& new_value
	) {
		return typesafe_sprintf(
			"%x %x %x",
			new_value ? "Set" : "Unset",
			field_name,
			describe_property_location()
		);
	};

	auto describe_changed_generic = [&](
		const auto& field_name,
		const auto& new_value
	) {
		return typesafe_sprintf(
			"Set %x to %x %x",
			field_name,
			new_value,
			describe_property_location()
		);
	};

	auto describe_changed = [&](
		const auto& field_name,
		const auto& old_value,
	   	const auto& new_value
	) {
		using F = std::decay_t<decltype(new_value)>;

		if constexpr(std::is_same_v<F, bool>) {
			return describe_changed_flag(field_name, new_value);
		}
		else {
			if (field_name == "Name") {
				return typesafe_sprintf(
					"Renamed %x to %x",
					old_value,
					new_value
				);
			}

			return describe_changed_generic(
				field_name,
				new_value
			);
		}
	};

	auto post_new_change = [&](
		const auto& description,
	   	const auto& new_content
	) {
		change_flavour_property_command cmd;
		cmd.property_id = make_property_id();

		cmd.value_after_change = augs::to_bytes(new_content);
		cmd.built_description = description;
		history.execute_new(cmd, in);
	};

	auto rewrite_last_change = [&](
		const auto& description,
		const auto& new_content
	) {
		auto& last = history.last_command();

		if (auto* const cmd = std::get_if<change_flavour_property_command>(std::addressof(last))) {
			cmd->built_description = description;
			cmd->rewrite_change(augs::to_bytes(new_content), in);
		}
		else {
			LOG("WARNING! There was some problem with tracking activity of editor controls.");
		}
	};

	auto handle_continuous_tweaker = [&](
		const auto& field_name,
	   	const auto& old_value,
		const auto& new_value,
	   	auto callback
	) {
		auto& wnd = *ImGui::GetCurrentWindow();

		if (callback()) {
			const auto this_id = ImGui::GetActiveID();
			const auto description = describe_changed(field_name, old_value, new_value);

			if (last_active != this_id) {
				/* LOG("Started dragging %x to %x", label, member); */
				post_new_change(description, new_value);
			}
			else {
				/* LOG("Dragging %x to %x", label, member); */
				rewrite_last_change(description, new_value);
			}

			last_active = this_id;
		}
		else {
			if (last_active == wnd.GetID(field_name.c_str()) && !ImGui::IsItemActive()) {
				/* LOG("Released %x to %x", label, member); */
				last_active.reset();
			}
		}
	};

	auto handle_discrete_tweaker = [&](
		const auto& field_name,
	   	const auto& old_value,
		const auto& new_value,
	   	auto callback
	) {
		if (callback()) {
			post_new_change(
				describe_changed(field_name, old_value, new_value),
				new_value
			);
		}
	};

	augs::introspect(
		augs::recursive([&](auto self, const std::string& original_label, const auto& original_member) {
			using M = std::decay_t<decltype(original_member)>;

			current_offset = static_cast<unsigned>(
				reinterpret_cast<const std::byte*>(std::addressof(original_member))
				- reinterpret_cast<const std::byte*>(std::addressof(invariant))
			);

			if constexpr(std::is_trivially_copyable_v<M>) {
				current_type_id.set<augs::trivial_type_marker>();
			}
			else if constexpr(is_container_v<M>) {
				current_type_id.set<M>();
			}
			else {
				static_assert(has_introspect_v<M>);
			}

			auto altered_member = original_member;

			const auto label = format_field_name(original_label);

			auto do_continuous = [&](auto&& f) {
				handle_continuous_tweaker(label, original_member, altered_member, std::forward<decltype(f)>(f));
			};

			auto do_discrete = [&](auto&& f) {
				handle_discrete_tweaker(label, original_member, altered_member, std::forward<decltype(f)>(f));
			};

			if constexpr(is_padding_field_v<M>) {
				return;
			}
			else if constexpr(std::is_same_v<M, b2Filter>) {
				// TODO: checkbox matrix
				return;
			}
			else if constexpr(std::is_same_v<M, std::string>) {
				do_continuous([&]() {
					if (original_label == "description") {
						return input_multiline_text<512>("Description", altered_member, 16);
					}
					else if (original_label == "name") {
						return input_text<256>("Name", altered_member);
					}

					return input_text<256>(label, altered_member);
				});

				details_text();
			}
			else if constexpr(std::is_same_v<M, flip_flags>) {
				auto do_flag = [&](const auto& flag_name, auto& flag) {
					if (checkbox(flag_name, flag)) {
						post_new_change(
							describe_changed_flag(flag_name, flag),
							altered_member
						);
					}

					details_text();
				};

				do_flag("Flip horizontally", altered_member[0]);
				do_flag("Flip vertically", altered_member[1]);
			}
			else if constexpr(std::is_same_v<M, bool>) {
				do_discrete([&]() {
					return checkbox(label, altered_member);
				});

				details_text();
			}
			else if constexpr(is_container_v<M>) {

			}
			else if constexpr(std::is_arithmetic_v<M>) {
				do_continuous([&]() {
					return drag(label, altered_member);
				});

				details_text(get_type_name<M>());
			}
			else if constexpr(std::is_enum_v<M>) {

			}
			else if constexpr(std::is_same_v<M, rgba>) {
				do_continuous([&]() {
					return color_edit(label, altered_member);
				});

				details_text();
			}
			else {
				const auto object_node = scoped_tree_node_ex(label.c_str());

				details_text(get_type_name<M>());

				if (object_node) {
					augs::introspect(augs::recursive(self), original_member);
				}
			}
		}),
		invariant
	);
}

template <class E>
void edit_flavour(
	const typed_entity_flavour_id<E> flavour_id,
	const entity_flavour<E>& flavour,
   	const editor_command_input in
) {
	edit_invariant(flavour.template get<invariants::name>(), flavour, flavour_id, in);

	for_each_through_std_get(
		flavour.invariants,
		[&](auto& invariant) {
			using T = std::decay_t<decltype(invariant)>;

			if constexpr(std::is_same_v<T, invariants::name>) {
				/* This one is handled already */
				return;
			}

			const auto invariant_label = get_invariant_stem(invariant) + " invariant";

			if (const auto node = scoped_tree_node_ex(invariant_label.c_str())) {
				edit_invariant(invariant, flavour, flavour_id, in);
			}

			ImGui::NextColumn();
			ImGui::NextColumn();
		}
   	);
}

void editor_all_entities_gui::open() {
	show = true;
	acquire_once = true;
	ImGui::SetWindowFocus("All entities");
}

void editor_all_entities_gui::perform(const editor_command_input in) {
	if (!show) {
		return;
	}

	using namespace augs::imgui;

	auto entities = scoped_window("All entities", &show);
	auto& work = *in.folder.work;
	auto& cosm = work.world;

	if (acquire_once) {
		ImGui::SetKeyboardFocusHere();
		acquire_once = false;
	}

	thread_local ImGuiTextFilter filter;
	filter.Draw();

	ImGui::Columns(2); // 4-ways, with border
	ImGui::NextColumn();
	text_disabled("Details");
	ImGui::NextColumn();
	ImGui::Separator();

	cosm.get_solvable().for_each_pool(
		[&](const auto& p){
			using P = decltype(p);
			using pool_type = std::decay_t<P>;

			using E = type_argument_t<typename pool_type::mapped_type>;

			const auto entity_type_label = format_field_name(get_type_name<E>());
			const auto total_entities = p.size();
			const auto total_flavours = cosm.get_common_significant().get_flavours<E>().count();

			ImGui::SetNextTreeNodeOpen(true, ImGuiCond_FirstUseEver);

			const auto node = scoped_tree_node_ex(entity_type_label.c_str());

			ImGui::NextColumn();
			text_disabled(typesafe_sprintf("%x Flavours, %x Entities", total_flavours, total_entities));
			ImGui::NextColumn();

			if (node) {
				cosm.change_common_significant([&](cosmos_common_significant& common_signi){
					common_signi.get_flavours<E>().for_each(
						[&](
							const auto flavour_id,
							/*
								Note: we accept flavour as const,
								because ImGUI itself should only see the immutable reference.

							   	Is the job of the change_flavour_property_command to actually alter flavour state.
							*/
						   	const auto& flavour
						){
							const auto flavour_label = flavour.template get<invariants::name>().name;

							if (!filter.PassFilter(flavour_label.c_str())) {
								return;
							}

							const auto all_having_flavour = cosm.get_solvable_inferred().name.get_entities_by_flavour_id(flavour_id);

							const auto node_label = typesafe_sprintf("%x###%x", flavour_label, flavour_id.raw);
							const auto f_node = scoped_tree_node_ex(node_label.c_str());

							ImGui::NextColumn();
							text_disabled(typesafe_sprintf("%x Entities", all_having_flavour.size()));
							ImGui::NextColumn();

							if (f_node) {
								ImGui::Separator();
								edit_flavour(flavour_id, flavour, in);
								ImGui::Separator();

								for (const auto& e : all_having_flavour) {
									bool s = false;
									ImGui::Selectable(typesafe_sprintf("%x", cosm[e].get_guid()).c_str(), &s);

									ImGui::NextColumn();
									ImGui::NextColumn();
								}
							}
						}
					);

					return changer_callback_result::DONT_REFRESH;
				});
			}
		}
	);
}
<commit_msg>fixed describer of name changed command<commit_after>#include "augs/templates/for_each_std_get.h"

#include "augs/readwrite/memory_stream.h"

#include "augs/misc/imgui/imgui_utils.h"
#include "augs/misc/imgui/imgui_scope_wrappers.h"
#include "augs/misc/imgui/imgui_control_wrappers.h"

#include "application/intercosm.h"
#include "application/setups/editor/editor_folder.h"
#include "application/setups/editor/gui/editor_all_entities_gui.h"

#include "application/setups/editor/gui/editor_properties_gui.h"

#include "augs/readwrite/memory_stream.h"
#include "augs/readwrite/byte_readwrite.h"

using namespace augs::imgui;

static auto details_text = [](const std::string& tx = ""){
	ImGui::NextColumn();

	if (tx.size() > 0) {
		text(tx);
	}

	ImGui::NextColumn();
};

template <class T>
auto get_invariant_stem(const T&) {
	auto result = format_field_name(get_type_name_strip_namespace<T>());
	result[0] = std::toupper(result[0]);

	/* These two look ugly with automated names */

	if constexpr(std::is_same_v<T, invariants::sprite>) {
		result = "Sprite";
	}

	if constexpr(std::is_same_v<T, invariants::polygon>) {
		result = "Polygon";
	}

	return result;
}

template <class T>
decltype(auto) get_name_of(const entity_flavour<T>& flavour) {
	return flavour.template get<invariants::name>().name;
}

template <class T, class E>
void edit_invariant(
	const T& invariant,
	const entity_flavour<E>& source_flavour,
	const typed_entity_flavour_id<E> flavour_id,
   	const editor_command_input in
) {
	thread_local std::optional<ImGuiID> last_active;
	thread_local std::string cached_old_description;

	auto& history = in.folder.history;

	auto current_offset = static_cast<unsigned>(-1);
	auto current_type_id = edited_field_type_id();

	auto make_property_id = [&](){
		flavour_property_id result;
		result.field_offset = current_offset;
		result.field_type = current_type_id;
		result.invariant_id = index_in_list_v<T, decltype(entity_flavour<E>::invariants)>;
		result.flavour_id = flavour_id;

		return result;
	};

	const auto describe_property_location = [&](){
		const auto flavour_name = get_name_of(source_flavour);
		const auto invariant_name = get_invariant_stem(invariant);

		return typesafe_sprintf("(in %x of %x)", invariant_name, flavour_name);
	};

	/*
		Change description format:

		[address] = Muzzle velocity (in Gun of Amplifier arm)

		Bools:

		Set [address]
		Unset [address]

		Other fields:

		Set [address] to [value]

		Name:

		Renamed [old name] to [new name]
	*/

	auto describe_changed_flag = [&](
		const auto& field_name,
		const auto& new_value
	) {
		return typesafe_sprintf(
			"%x %x %x",
			new_value ? "Set" : "Unset",
			field_name,
			describe_property_location()
		);
	};

	auto describe_changed_generic = [&](
		const auto& field_name,
		const auto& new_value
	) {
		return typesafe_sprintf(
			"Set %x to %x %x",
			field_name,
			new_value,
			describe_property_location()
		);
	};

	struct description_pair {
		std::string of_old;
		std::string of_new;
	};

	auto describe_changed = [&](
		const auto& field_name,
		const auto& old_value,
	   	const auto& new_value
	) -> description_pair {
		using F = std::decay_t<decltype(new_value)>;

		if constexpr(std::is_same_v<F, bool>) {
			return { "", describe_changed_flag(field_name, new_value) };
		}
		else {
			if (field_name == "Name") {
				return {
					typesafe_sprintf("Renamed %x ", old_value),
					typesafe_sprintf("to %x ", new_value)
				};
			}

			return { "", describe_changed_generic(
				field_name,
				new_value
			) };
		}
	};

	auto post_new_change = [&](
		const description_pair& description,
	   	const auto& new_content
	) {
		cached_old_description = description.of_old;

		change_flavour_property_command cmd;
		cmd.property_id = make_property_id();

		cmd.value_after_change = augs::to_bytes(new_content);
		cmd.built_description = cached_old_description + description.of_new;
		history.execute_new(cmd, in);
	};

	auto rewrite_last_change = [&](
		const auto& description,
		const auto& new_content
	) {
		auto& last = history.last_command();

		if (auto* const cmd = std::get_if<change_flavour_property_command>(std::addressof(last))) {
			cmd->built_description = cached_old_description + description;
			cmd->rewrite_change(augs::to_bytes(new_content), in);
		}
		else {
			LOG("WARNING! There was some problem with tracking activity of editor controls.");
		}
	};

	auto handle_continuous_tweaker = [&](
		const auto& field_name,
	   	const auto& old_value,
		const auto& new_value,
	   	auto callback
	) {
		auto& wnd = *ImGui::GetCurrentWindow();

		if (callback()) {
			const auto this_id = ImGui::GetActiveID();
			const auto description = describe_changed(field_name, old_value, new_value);

			if (last_active != this_id) {
				/* LOG("Started dragging %x to %x", label, member); */
				post_new_change(description, new_value);
			}
			else {
				/* LOG("Dragging %x to %x", label, member); */
				rewrite_last_change(description.of_new, new_value);
			}

			last_active = this_id;
		}
		else {
			if (last_active == wnd.GetID(field_name.c_str()) && !ImGui::IsItemActive()) {
				/* LOG("Released %x to %x", label, member); */
				last_active.reset();
			}
		}
	};

	auto handle_discrete_tweaker = [&](
		const auto& field_name,
	   	const auto& old_value,
		const auto& new_value,
	   	auto callback
	) {
		if (callback()) {
			post_new_change(
				describe_changed(field_name, old_value, new_value),
				new_value
			);
		}
	};

	augs::introspect(
		augs::recursive([&](auto self, const std::string& original_label, const auto& original_member) {
			using M = std::decay_t<decltype(original_member)>;

			current_offset = static_cast<unsigned>(
				reinterpret_cast<const std::byte*>(std::addressof(original_member))
				- reinterpret_cast<const std::byte*>(std::addressof(invariant))
			);

			if constexpr(std::is_trivially_copyable_v<M>) {
				current_type_id.set<augs::trivial_type_marker>();
			}
			else if constexpr(is_container_v<M>) {
				current_type_id.set<M>();
			}
			else {
				static_assert(has_introspect_v<M>);
			}

			auto altered_member = original_member;

			const auto label = format_field_name(original_label);

			auto do_continuous = [&](auto&& f) {
				handle_continuous_tweaker(label, original_member, altered_member, std::forward<decltype(f)>(f));
			};

			auto do_discrete = [&](auto&& f) {
				handle_discrete_tweaker(label, original_member, altered_member, std::forward<decltype(f)>(f));
			};

			if constexpr(is_padding_field_v<M>) {
				return;
			}
			else if constexpr(std::is_same_v<M, b2Filter>) {
				// TODO: checkbox matrix
				return;
			}
			else if constexpr(std::is_same_v<M, std::string>) {
				do_continuous([&]() {
					if (original_label == "description") {
						return input_multiline_text<512>("Description", altered_member, 16);
					}
					else if (original_label == "name") {
						return input_text<256>("Name", altered_member);
					}

					return input_text<256>(label, altered_member);
				});

				details_text();
			}
			else if constexpr(std::is_same_v<M, flip_flags>) {
				auto do_flag = [&](const auto& flag_name, auto& flag) {
					if (checkbox(flag_name, flag)) {
						post_new_change(
							{ "", describe_changed_flag(flag_name, flag) },
							altered_member
						);
					}

					details_text();
				};

				do_flag("Flip horizontally", altered_member[0]);
				do_flag("Flip vertically", altered_member[1]);
			}
			else if constexpr(std::is_same_v<M, bool>) {
				do_discrete([&]() {
					return checkbox(label, altered_member);
				});

				details_text();
			}
			else if constexpr(is_container_v<M>) {

			}
			else if constexpr(std::is_arithmetic_v<M>) {
				do_continuous([&]() {
					return drag(label, altered_member);
				});

				details_text(get_type_name<M>());
			}
			else if constexpr(std::is_enum_v<M>) {

			}
			else if constexpr(std::is_same_v<M, rgba>) {
				do_continuous([&]() {
					return color_edit(label, altered_member);
				});

				details_text();
			}
			else {
				const auto object_node = scoped_tree_node_ex(label.c_str());

				details_text(get_type_name<M>());

				if (object_node) {
					augs::introspect(augs::recursive(self), original_member);
				}
			}
		}),
		invariant
	);
}

template <class E>
void edit_flavour(
	const typed_entity_flavour_id<E> flavour_id,
	const entity_flavour<E>& flavour,
   	const editor_command_input in
) {
	edit_invariant(flavour.template get<invariants::name>(), flavour, flavour_id, in);

	for_each_through_std_get(
		flavour.invariants,
		[&](auto& invariant) {
			using T = std::decay_t<decltype(invariant)>;

			if constexpr(std::is_same_v<T, invariants::name>) {
				/* This one is handled already */
				return;
			}

			const auto invariant_label = get_invariant_stem(invariant) + " invariant";

			if (const auto node = scoped_tree_node_ex(invariant_label.c_str())) {
				edit_invariant(invariant, flavour, flavour_id, in);
			}

			ImGui::NextColumn();
			ImGui::NextColumn();
		}
   	);
}

void editor_all_entities_gui::open() {
	show = true;
	acquire_once = true;
	ImGui::SetWindowFocus("All entities");
}

void editor_all_entities_gui::perform(const editor_command_input in) {
	if (!show) {
		return;
	}

	using namespace augs::imgui;

	auto entities = scoped_window("All entities", &show);
	auto& work = *in.folder.work;
	auto& cosm = work.world;

	if (acquire_once) {
		ImGui::SetKeyboardFocusHere();
		acquire_once = false;
	}

	thread_local ImGuiTextFilter filter;
	filter.Draw();

	ImGui::Columns(2); // 4-ways, with border
	ImGui::NextColumn();
	text_disabled("Details");
	ImGui::NextColumn();
	ImGui::Separator();

	cosm.get_solvable().for_each_pool(
		[&](const auto& p){
			using P = decltype(p);
			using pool_type = std::decay_t<P>;

			using E = type_argument_t<typename pool_type::mapped_type>;

			const auto entity_type_label = format_field_name(get_type_name<E>());
			const auto total_entities = p.size();
			const auto total_flavours = cosm.get_common_significant().get_flavours<E>().count();

			ImGui::SetNextTreeNodeOpen(true, ImGuiCond_FirstUseEver);

			const auto node = scoped_tree_node_ex(entity_type_label.c_str());

			ImGui::NextColumn();
			text_disabled(typesafe_sprintf("%x Flavours, %x Entities", total_flavours, total_entities));
			ImGui::NextColumn();

			if (node) {
				cosm.change_common_significant([&](cosmos_common_significant& common_signi){
					common_signi.get_flavours<E>().for_each(
						[&](
							const auto flavour_id,
							/*
								Note: we accept flavour as const,
								because ImGUI itself should only see the immutable reference.

							   	Is the job of the change_flavour_property_command to actually alter flavour state.
							*/
						   	const auto& flavour
						){
							const auto flavour_label = flavour.template get<invariants::name>().name;

							if (!filter.PassFilter(flavour_label.c_str())) {
								return;
							}

							const auto all_having_flavour = cosm.get_solvable_inferred().name.get_entities_by_flavour_id(flavour_id);

							const auto node_label = typesafe_sprintf("%x###%x", flavour_label, flavour_id.raw);
							const auto f_node = scoped_tree_node_ex(node_label.c_str());

							ImGui::NextColumn();
							text_disabled(typesafe_sprintf("%x Entities", all_having_flavour.size()));
							ImGui::NextColumn();

							if (f_node) {
								ImGui::Separator();
								edit_flavour(flavour_id, flavour, in);
								ImGui::Separator();

								for (const auto& e : all_having_flavour) {
									bool s = false;
									ImGui::Selectable(typesafe_sprintf("%x", cosm[e].get_guid()).c_str(), &s);

									ImGui::NextColumn();
									ImGui::NextColumn();
								}
							}
						}
					);

					return changer_callback_result::DONT_REFRESH;
				});
			}
		}
	);
}
<|endoftext|>