<commit_before>#include <errno.h>

#include <iostream>
#include <string>

using namespace std;

#include "config.h"

#include <openssl/rand.h>
#include "common/common_init.h"

#include "common/base64.h"
#include "rgw_user.h"
#include "rgw_access.h"
#include "rgw_acl.h"

#define SECRET_KEY_LEN 40
#define PUBLIC_ID_LEN 20

void usage()
{
  cerr << "usage: rgw_admin <--user-gen | --user-modify | --read-policy | --list-buckets > [options...]" << std::endl;
  cerr << "options:" << std::endl;
  cerr << "   --uid=<id>" << std::endl;
  cerr << "   --key=<key>" << std::endl;
  cerr << "   --email=<email>" << std::endl;
  cerr << "   --display-name=<name>" << std::endl;
  cerr << "   --bucket=<bucket>" << std::endl;
  cerr << "   --object=<object>" << std::endl;
  generic_usage();
}

int gen_rand_base64(char *dest, int size) /* size should be the required string size + 1 */
{
  unsigned char buf[size];
  char tmp_dest[size + 4]; /* so that there's space for the extra '=' characters, and some */

  int ret = RAND_bytes(buf, sizeof(buf));
  if (!ret) {
    cerr << "RAND_bytes failed, entropy problem?" << std::endl;
    return -1;
  }

  ret = encode_base64((const char *)buf, ((size - 1) * 3 + 4 - 1) / 4, tmp_dest, sizeof(tmp_dest));
  if (ret < 0) {
    cerr << "encode_base64 failed" << std::endl;
    return -1;
  }
  memcpy(dest, tmp_dest, size);
  dest[size] = '\0';

  return 0;
}

static const char alphanum_table[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

int gen_rand_alphanumeric(char *dest, int size) /* size should be the required string size + 1 */
{
  int ret = RAND_bytes((unsigned char *)dest, size);
  if (!ret) {
    cerr << "RAND_bytes failed, entropy problem?" << std::endl;
    return -1;
  }

  int i;
  for (i=0; i<size - 1; i++) {
    int pos = (unsigned)dest[i];
    dest[i] = alphanum_table[pos % (sizeof(alphanum_table) - 1)];
  }
  dest[i] = '\0';

  return 0;
}

#if 0
static int rebuild_policy(RGWAccessControlPolicy& src, RGWAccessControlPolicy& dest)
{
  ACLOwner *owner = (ACLOwner *)src.find_first("Owner");
  if (!owner)
    return -EINVAL;

  RGWUserInfo owner_info;
  if (rgw_get_user_info(owner->get_id(), owner_info) < 0) {
    cerr << "owner info does not exist" << std::endl;
    return -EINVAL;
  }
  ACLOwner& new_owner = dest.get_owner();
  new_owner.set_id(owner->get_id());
  new_owner.set_name(owner_info.display_name);

  RGWAccessControlList& src_acl = src.get_acl();
  RGWAccessControlList& acl = dest.get_acl();

  XMLObjIter iter = src_acl.find("Grant");
  ACLGrant *src_grant = (ACLGrant *)iter.get_next();
  while (src_grant) {
    string id = src_grant->get_id();

    RGWUserInfo grant_user;
    if (rgw_get_user_info(id, grant_user) < 0) {
      cerr << "grant user does not exist:" << id << std::endl;
    } else {
      ACLGrant new_grant;
      ACLPermission& perm = src_grant->get_permission();
      new_grant.set_canon(id, grant_user.display_name, perm.get_permissions());
      cerr << "new grant: " << id << ":" << grant_user.display_name << std::endl;
      acl.add_grant(&new_grant);
    }
    src_grant = (ACLGrant *)iter.get_next();
  }

  return 0;
}
#endif

int main(int argc, char **argv)
{
  DEFINE_CONF_VARS(usage);
  vector<const char*> args;
  argv_to_vec(argc, (const char **)argv, args);
  env_to_vec(args);
  common_init(args, "rgw", true, true);

  const char *user_id = 0;
  const char *secret_key = 0;
  const char *user_email = 0;
  const char *display_name = 0;
  const char *bucket = 0;
  const char *object = 0;
  bool gen_user = false;
  bool mod_user = false;
  bool read_policy = false;
  bool list_buckets = false;
  int actions = 0 ;
  RGWUserInfo info;
  RGWAccess *store;

  if (g_conf.clock_tare) g_clock.tare();

  FOR_EACH_ARG(args) {
    if (CONF_ARG_EQ("user-gen", 'g')) {
      gen_user = true;
    } else if (CONF_ARG_EQ("user-modify", 'm')) {
      mod_user = true;
    } else if (CONF_ARG_EQ("read-policy", 'p')) {
      read_policy = true;
    } else if (CONF_ARG_EQ("list-buckets", 'l')) {
      list_buckets = true;
    } else if (CONF_ARG_EQ("uid", 'i')) {
      CONF_SAFE_SET_ARG_VAL(&user_id, OPT_STR);
    } else if (CONF_ARG_EQ("key", 'k')) {
      CONF_SAFE_SET_ARG_VAL(&secret_key, OPT_STR);
    } else if (CONF_ARG_EQ("email", 'e')) {
      CONF_SAFE_SET_ARG_VAL(&user_email, OPT_STR);
    } else if (CONF_ARG_EQ("display-name", 'n')) {
      CONF_SAFE_SET_ARG_VAL(&display_name, OPT_STR);
    } else if (CONF_ARG_EQ("bucket", 'b')) {
      CONF_SAFE_SET_ARG_VAL(&bucket, OPT_STR);
    } else if (CONF_ARG_EQ("object", 'o')) {
      CONF_SAFE_SET_ARG_VAL(&object, OPT_STR);
    } else {
      cerr << "unrecognized arg " << args[i] << std::endl;
      ARGS_USAGE();
    }
  }

  store = RGWAccess::init_storage_provider("rados", argc, argv);
  if (!store) {
    cerr << "couldn't init storage provider" << std::endl;
  }

  if (mod_user) {
    actions++;

    if (!user_id) {
      cerr << "user_id was not specified, aborting" << std::endl;
      return 0;
    }

    string user_id_str = user_id;

    if (rgw_get_user_info(user_id_str, info) < 0) {
      cerr << "error reading user info, aborting" << std::endl;
      exit(1);
    }
  }

  if (gen_user) {
    actions++;
    char secret_key_buf[SECRET_KEY_LEN + 1];
    char public_id_buf[PUBLIC_ID_LEN + 1];
    int ret;

    if (!display_name) {
      cerr << "display name was not specified, aborting" << std::endl;
      return 0;
    }

    if (!secret_key) {
      ret = gen_rand_base64(secret_key_buf, sizeof(secret_key_buf));
      if (ret < 0) {
        cerr << "aborting" << std::endl;
        exit(1);
      }
      secret_key = secret_key_buf;
    }
    if (!user_id) {
      ret = gen_rand_alphanumeric(public_id_buf, sizeof(public_id_buf));
      if (ret < 0) {
        cerr << "aborting" << std::endl;
        exit(1);
      }
      user_id = public_id_buf;
    }
  }

  if (gen_user || mod_user) {
    if (user_id)
      info.user_id = user_id;
    if (secret_key)
      info.secret_key = secret_key;
    if (display_name)
      info.display_name = display_name;
    if (user_email)
      info.user_email = user_email;

    int err;
    if ((err = rgw_store_user_info(info)) < 0) {
      cerr << "error storing user info" << strerror(-err) << std::endl;
    } else {
      cout << "User ID: " << info.user_id << std::endl;
      cout << "Secret Key: " << info.secret_key << std::endl;
      cout << "Display Name: " << info.display_name << std::endl;
    }
  }

  if (read_policy) {
    actions++;
    bufferlist bl;
    if (!bucket)
      bucket = "";
    if (!object)
      object = "";
    string bucket_str(bucket);
    string object_str(object);
    int ret = store->get_attr(bucket_str, object_str,
                       RGW_ATTR_ACL, bl);

    RGWAccessControlPolicy policy;
    if (ret >= 0) {
      bufferlist::iterator iter = bl.begin();
      policy.decode(iter);
      policy.to_xml(cout);
      cout << std::endl;
    }
  }

  if (list_buckets) {
    actions++;
    string id;
    RGWAccessHandle handle;

    if (user_id) {
      RGWUserBuckets buckets;
      if (rgw_get_user_buckets(user_id, buckets) < 0) {
        cout << "could not get buckets for uid " << user_id << std::endl;
      } else {
        cout << "listing buckets for uid " << user_id << std::endl;
        map<string, RGWObjEnt>& m = buckets.get_buckets();
        map<string, RGWObjEnt>::iterator iter;

        for (iter = m.begin(); iter != m.end(); ++iter) {
          RGWObjEnt obj = iter->second;
          cout << obj.name << std::endl;
        }
      }
    } else {
      if (store->list_buckets_init(id, &handle) < 0) {
        cout << "list-buckets: no entries found" << std::endl;
      } else {
        RGWObjEnt obj;
        cout << "listing all buckets" << std::endl;
        while (store->list_buckets_next(id, obj, &handle) >= 0) {
          cout << obj.name << std::endl;
        }
      }
    }
  }

  if (!actions)
    ARGS_USAGE();

  return 0;
}

<commit_msg>rgw: set auid if specified at creation<commit_after>#include <errno.h>

#include <iostream>
#include <string>

using namespace std;

#include "config.h"

#include <openssl/rand.h>
#include "common/common_init.h"

#include "common/base64.h"
#include "rgw_user.h"
#include "rgw_access.h"
#include "rgw_acl.h"

#define SECRET_KEY_LEN 40
#define PUBLIC_ID_LEN 20

void usage()
{
  cerr << "usage: rgw_admin <--user-gen | --user-modify | --read-policy | --list-buckets > [options...]" << std::endl;
  cerr << "options:" << std::endl;
  cerr << "   --uid=<id> (S3 uid)" << std::endl;
  cerr << "   --auth_uid=<auid> (librados uid)" << std::endl;
  cerr << "   --key=<key>" << std::endl;
  cerr << "   --email=<email>" << std::endl;
  cerr << "   --display-name=<name>" << std::endl;
  cerr << "   --bucket=<bucket>" << std::endl;
  cerr << "   --object=<object>" << std::endl;
  generic_usage();
}

int gen_rand_base64(char *dest, int size) /* size should be the required string size + 1 */
{
  unsigned char buf[size];
  char tmp_dest[size + 4]; /* so that there's space for the extra '=' characters, and some */

  int ret = RAND_bytes(buf, sizeof(buf));
  if (!ret) {
    cerr << "RAND_bytes failed, entropy problem?" << std::endl;
    return -1;
  }

  ret = encode_base64((const char *)buf, ((size - 1) * 3 + 4 - 1) / 4, tmp_dest, sizeof(tmp_dest));
  if (ret < 0) {
    cerr << "encode_base64 failed" << std::endl;
    return -1;
  }
  memcpy(dest, tmp_dest, size);
  dest[size] = '\0';

  return 0;
}

static const char alphanum_table[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

int gen_rand_alphanumeric(char *dest, int size) /* size should be the required string size + 1 */
{
  int ret = RAND_bytes((unsigned char *)dest, size);
  if (!ret) {
    cerr << "RAND_bytes failed, entropy problem?" << std::endl;
    return -1;
  }

  int i;
  for (i=0; i<size - 1; i++) {
    int pos = (unsigned)dest[i];
    dest[i] = alphanum_table[pos % (sizeof(alphanum_table) - 1)];
  }
  dest[i] = '\0';

  return 0;
}

#if 0
static int rebuild_policy(RGWAccessControlPolicy& src, RGWAccessControlPolicy& dest)
{
  ACLOwner *owner = (ACLOwner *)src.find_first("Owner");
  if (!owner)
    return -EINVAL;

  RGWUserInfo owner_info;
  if (rgw_get_user_info(owner->get_id(), owner_info) < 0) {
    cerr << "owner info does not exist" << std::endl;
    return -EINVAL;
  }
  ACLOwner& new_owner = dest.get_owner();
  new_owner.set_id(owner->get_id());
  new_owner.set_name(owner_info.display_name);

  RGWAccessControlList& src_acl = src.get_acl();
  RGWAccessControlList& acl = dest.get_acl();

  XMLObjIter iter = src_acl.find("Grant");
  ACLGrant *src_grant = (ACLGrant *)iter.get_next();
  while (src_grant) {
    string id = src_grant->get_id();

    RGWUserInfo grant_user;
    if (rgw_get_user_info(id, grant_user) < 0) {
      cerr << "grant user does not exist:" << id << std::endl;
    } else {
      ACLGrant new_grant;
      ACLPermission& perm = src_grant->get_permission();
      new_grant.set_canon(id, grant_user.display_name, perm.get_permissions());
      cerr << "new grant: " << id << ":" << grant_user.display_name << std::endl;
      acl.add_grant(&new_grant);
    }
    src_grant = (ACLGrant *)iter.get_next();
  }

  return 0;
}
#endif

int main(int argc, char **argv)
{
  DEFINE_CONF_VARS(usage);
  vector<const char*> args;
  argv_to_vec(argc, (const char **)argv, args);
  env_to_vec(args);
  common_init(args, "rgw", true, true);

  const char *user_id = 0;
  const char *secret_key = 0;
  const char *user_email = 0;
  const char *display_name = 0;
  const char *bucket = 0;
  const char *object = 0;
  bool gen_user = false;
  bool mod_user = false;
  bool read_policy = false;
  bool list_buckets = false;
  int actions = 0 ;
  __u64 auid = 0;
  RGWUserInfo info;
  RGWAccess *store;

  if (g_conf.clock_tare) g_clock.tare();

  FOR_EACH_ARG(args) {
    if (CONF_ARG_EQ("user-gen", 'g')) {
      gen_user = true;
    } else if (CONF_ARG_EQ("user-modify", 'm')) {
      mod_user = true;
    } else if (CONF_ARG_EQ("read-policy", 'p')) {
      read_policy = true;
    } else if (CONF_ARG_EQ("list-buckets", 'l')) {
      list_buckets = true;
    } else if (CONF_ARG_EQ("uid", 'i')) {
      CONF_SAFE_SET_ARG_VAL(&user_id, OPT_STR);
    } else if (CONF_ARG_EQ("key", 'k')) {
      CONF_SAFE_SET_ARG_VAL(&secret_key, OPT_STR);
    } else if (CONF_ARG_EQ("email", 'e')) {
      CONF_SAFE_SET_ARG_VAL(&user_email, OPT_STR);
    } else if (CONF_ARG_EQ("display-name", 'n')) {
      CONF_SAFE_SET_ARG_VAL(&display_name, OPT_STR);
    } else if (CONF_ARG_EQ("bucket", 'b')) {
      CONF_SAFE_SET_ARG_VAL(&bucket, OPT_STR);
    } else if (CONF_ARG_EQ("object", 'o')) {
      CONF_SAFE_SET_ARG_VAL(&object, OPT_STR);
    } else if (CONF_ARG_EQ("auth_uid", 'a')) {
      CONF_SAFE_SET_ARG_VAL(&auid, OPT_LONGLONG);
    } else {
      cerr << "unrecognized arg " << args[i] << std::endl;
      ARGS_USAGE();
    }
  }

  store = RGWAccess::init_storage_provider("rados", argc, argv);
  if (!store) {
    cerr << "couldn't init storage provider" << std::endl;
  }

  if (mod_user) {
    actions++;

    if (!user_id) {
      cerr << "user_id was not specified, aborting" << std::endl;
      return 0;
    }

    string user_id_str = user_id;

    if (rgw_get_user_info(user_id_str, info) < 0) {
      cerr << "error reading user info, aborting" << std::endl;
      exit(1);
    }
  }

  if (gen_user) {
    actions++;
    char secret_key_buf[SECRET_KEY_LEN + 1];
    char public_id_buf[PUBLIC_ID_LEN + 1];
    int ret;

    if (!display_name) {
      cerr << "display name was not specified, aborting" << std::endl;
      return 0;
    }

    if (!secret_key) {
      ret = gen_rand_base64(secret_key_buf, sizeof(secret_key_buf));
      if (ret < 0) {
        cerr << "aborting" << std::endl;
        exit(1);
      }
      secret_key = secret_key_buf;
    }
    if (!user_id) {
      ret = gen_rand_alphanumeric(public_id_buf, sizeof(public_id_buf));
      if (ret < 0) {
        cerr << "aborting" << std::endl;
        exit(1);
      }
      user_id = public_id_buf;
    }
  }

  if (gen_user || mod_user) {
    if (user_id)
      info.user_id = user_id;
    if (secret_key)
      info.secret_key = secret_key;
    if (display_name)
      info.display_name = display_name;
    if (user_email)
      info.user_email = user_email;
    if (auid)
      info.auid = auid;

    int err;
    if ((err = rgw_store_user_info(info)) < 0) {
      cerr << "error storing user info" << strerror(-err) << std::endl;
    } else {
      cout << "User ID: " << info.user_id << std::endl;
      cout << "Secret Key: " << info.secret_key << std::endl;
      cout << "Display Name: " << info.display_name << std::endl;
    }
  }

  if (read_policy) {
    actions++;
    bufferlist bl;
    if (!bucket)
      bucket = "";
    if (!object)
      object = "";
    string bucket_str(bucket);
    string object_str(object);
    int ret = store->get_attr(bucket_str, object_str,
                       RGW_ATTR_ACL, bl);

    RGWAccessControlPolicy policy;
    if (ret >= 0) {
      bufferlist::iterator iter = bl.begin();
      policy.decode(iter);
      policy.to_xml(cout);
      cout << std::endl;
    }
  }

  if (list_buckets) {
    actions++;
    string id;
    RGWAccessHandle handle;

    if (user_id) {
      RGWUserBuckets buckets;
      if (rgw_get_user_buckets(user_id, buckets) < 0) {
        cout << "could not get buckets for uid " << user_id << std::endl;
      } else {
        cout << "listing buckets for uid " << user_id << std::endl;
        map<string, RGWObjEnt>& m = buckets.get_buckets();
        map<string, RGWObjEnt>::iterator iter;

        for (iter = m.begin(); iter != m.end(); ++iter) {
          RGWObjEnt obj = iter->second;
          cout << obj.name << std::endl;
        }
      }
    } else {
      if (store->list_buckets_init(id, &handle) < 0) {
        cout << "list-buckets: no entries found" << std::endl;
      } else {
        RGWObjEnt obj;
        cout << "listing all buckets" << std::endl;
        while (store->list_buckets_next(id, obj, &handle) >= 0) {
          cout << obj.name << std::endl;
        }
      }
    }
  }

  if (!actions)
    ARGS_USAGE();

  return 0;
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2020 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "mutation_reader.hh"

class test_reader_lifecycle_policy
        : public reader_lifecycle_policy
        , public enable_shared_from_this<test_reader_lifecycle_policy> {
public:
    class operations_gate {
    public:
        class operation {
            gate* _g = nullptr;

        private:
            void leave() {
                if (_g) {
                    _g->leave();
                }
            }

        public:
            operation() = default;
            explicit operation(gate& g) : _g(&g) { _g->enter(); }
            operation(const operation&) = delete;
            operation(operation&& o) : _g(std::exchange(o._g, nullptr)) { }
            ~operation() { leave(); }
            operation& operator=(const operation&) = delete;
            operation& operator=(operation&& o) {
                leave();
                _g = std::exchange(o._g, nullptr);
                return *this;
            }
        };

    private:
        std::vector<gate> _gates;

    public:
        operations_gate()
            : _gates(smp::count) {
        }

        operation enter() {
            return operation(_gates[this_shard_id()]);
        }

        future<> close() {
            return parallel_for_each(boost::irange(smp::count), [this] (shard_id shard) {
                return smp::submit_to(shard, [this, shard] {
                    return _gates[shard].close();
                });
            });
        }
    };

private:
    using factory_function = std::function<flat_mutation_reader(
            schema_ptr,
            const dht::partition_range&,
            const query::partition_slice&,
            const io_priority_class&,
            tracing::trace_state_ptr,
            mutation_reader::forwarding)>;

    struct reader_context {
        std::unique_ptr<reader_concurrency_semaphore> semaphore;
        operations_gate::operation op;
        std::optional<reader_permit> permit;
        std::optional<future<reader_permit::resource_units>> wait_future;
        std::optional<const dht::partition_range> range;
        std::optional<const query::partition_slice> slice;

        reader_context() = default;
        reader_context(dht::partition_range range, query::partition_slice slice) : range(std::move(range)), slice(std::move(slice)) {
        }
    };

    factory_function _factory_function;
    operations_gate& _operation_gate;
    std::vector<foreign_ptr<std::unique_ptr<reader_context>>> _contexts;
    std::vector<future<>> _destroy_futures;
    bool _evict_paused_readers = false;

public:
    explicit test_reader_lifecycle_policy(factory_function f, operations_gate& g, bool evict_paused_readers = false)
        : _factory_function(std::move(f))
        , _operation_gate(g)
        , _contexts(smp::count)
        , _evict_paused_readers(evict_paused_readers) {
    }
    virtual flat_mutation_reader create_reader(
            schema_ptr schema,
            const dht::partition_range& range,
            const query::partition_slice& slice,
            const io_priority_class& pc,
            tracing::trace_state_ptr trace_state,
            mutation_reader::forwarding fwd_mr) override {
        const auto shard = this_shard_id();
        if (_contexts[shard]) {
            _contexts[shard]->range.emplace(range);
            _contexts[shard]->slice.emplace(slice);
        } else {
            _contexts[shard] = make_foreign(std::make_unique<reader_context>(range, slice));
        }
        _contexts[shard]->op = _operation_gate.enter();
        return _factory_function(std::move(schema), *_contexts[shard]->range, *_contexts[shard]->slice, pc, std::move(trace_state), fwd_mr);
    }
    virtual void destroy_reader(shard_id shard, future<stopped_reader> reader) noexcept override {
        // Move to the background, waited via _operation_gate
        (void)reader.then([shard, this] (stopped_reader&& reader) {
            return smp::submit_to(shard, [handle = std::move(reader.handle), ctx = std::move(_contexts[shard])] () mutable {
                ctx->semaphore->unregister_inactive_read(std::move(*handle));
                ctx->semaphore->broken(std::make_exception_ptr(broken_semaphore{}));
                if (ctx->wait_future) {
                    return ctx->wait_future->then_wrapped([ctx = std::move(ctx)] (future<reader_permit::resource_units> f) mutable {
                        f.ignore_ready_future();
                        ctx->permit.reset(); // make sure it's destroyed before the semaphore
                    });
                }
                return make_ready_future<>();
            });
        }).finally([zis = shared_from_this()] {});
    }
    virtual reader_concurrency_semaphore& semaphore() override {
        const auto shard = this_shard_id();
        if (!_contexts[shard]) {
            _contexts[shard] = make_foreign(std::make_unique<reader_context>());
        } else if (_contexts[shard]->semaphore) {
            return *_contexts[shard]->semaphore;
        }
        if (_evict_paused_readers) {
            _contexts[shard]->semaphore = std::make_unique<reader_concurrency_semaphore>(0, std::numeric_limits<ssize_t>::max(),
                    format("reader_concurrency_semaphore @shard_id={}", shard));
            _contexts[shard]->permit = _contexts[shard]->semaphore->make_permit();
            // Add a waiter, so that all registered inactive reads are
            // immediately evicted.
            // We don't care about the returned future.
            _contexts[shard]->wait_future = _contexts[shard]->permit->wait_admission(1, db::no_timeout);
        } else {
            _contexts[shard]->semaphore = std::make_unique<reader_concurrency_semaphore>(reader_concurrency_semaphore::no_limits{});
        }
        return *_contexts[shard]->semaphore;
    }
};

<commit_msg>test/lib/reader_lifecycle_policy: don't destroy reader context eagerly<commit_after>/*
 * Copyright (C) 2020 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "mutation_reader.hh"

class test_reader_lifecycle_policy
        : public reader_lifecycle_policy
        , public enable_shared_from_this<test_reader_lifecycle_policy> {
public:
    class operations_gate {
    public:
        class operation {
            gate* _g = nullptr;

        private:
            void leave() {
                if (_g) {
                    _g->leave();
                }
            }

        public:
            operation() = default;
            explicit operation(gate& g) : _g(&g) { _g->enter(); }
            operation(const operation&) = delete;
            operation(operation&& o) : _g(std::exchange(o._g, nullptr)) { }
            ~operation() { leave(); }
            operation& operator=(const operation&) = delete;
            operation& operator=(operation&& o) {
                leave();
                _g = std::exchange(o._g, nullptr);
                return *this;
            }
        };

    private:
        std::vector<gate> _gates;

    public:
        operations_gate()
            : _gates(smp::count) {
        }

        operation enter() {
            return operation(_gates[this_shard_id()]);
        }

        future<> close() {
            return parallel_for_each(boost::irange(smp::count), [this] (shard_id shard) {
                return smp::submit_to(shard, [this, shard] {
                    return _gates[shard].close();
                });
            });
        }
    };

private:
    using factory_function = std::function<flat_mutation_reader(
            schema_ptr,
            const dht::partition_range&,
            const query::partition_slice&,
            const io_priority_class&,
            tracing::trace_state_ptr,
            mutation_reader::forwarding)>;

    struct reader_context {
        std::unique_ptr<reader_concurrency_semaphore> semaphore;
        operations_gate::operation op;
        std::optional<reader_permit> permit;
        std::optional<future<reader_permit::resource_units>> wait_future;
        std::optional<const dht::partition_range> range;
        std::optional<const query::partition_slice> slice;

        reader_context() = default;
        reader_context(dht::partition_range range, query::partition_slice slice) : range(std::move(range)), slice(std::move(slice)) {
        }
    };

    factory_function _factory_function;
    operations_gate& _operation_gate;
    std::vector<foreign_ptr<std::unique_ptr<reader_context>>> _contexts;
    std::vector<future<>> _destroy_futures;
    bool _evict_paused_readers = false;

public:
    explicit test_reader_lifecycle_policy(factory_function f, operations_gate& g, bool evict_paused_readers = false)
        : _factory_function(std::move(f))
        , _operation_gate(g)
        , _contexts(smp::count)
        , _evict_paused_readers(evict_paused_readers) {
    }
    virtual flat_mutation_reader create_reader(
            schema_ptr schema,
            const dht::partition_range& range,
            const query::partition_slice& slice,
            const io_priority_class& pc,
            tracing::trace_state_ptr trace_state,
            mutation_reader::forwarding fwd_mr) override {
        const auto shard = this_shard_id();
        if (_contexts[shard]) {
            _contexts[shard]->range.emplace(range);
            _contexts[shard]->slice.emplace(slice);
        } else {
            _contexts[shard] = make_foreign(std::make_unique<reader_context>(range, slice));
        }
        _contexts[shard]->op = _operation_gate.enter();
        return _factory_function(std::move(schema), *_contexts[shard]->range, *_contexts[shard]->slice, pc, std::move(trace_state), fwd_mr);
    }
    virtual void destroy_reader(shard_id shard, future<stopped_reader> reader) noexcept override {
        // Move to the background, waited via _operation_gate
        (void)reader.then([shard, this] (stopped_reader&& reader) {
            return smp::submit_to(shard, [handle = std::move(reader.handle), ctx = &*_contexts[shard]] () mutable {
                ctx->semaphore->unregister_inactive_read(std::move(*handle));
                ctx->semaphore->broken(std::make_exception_ptr(broken_semaphore{}));
                if (ctx->wait_future) {
                    return ctx->wait_future->then_wrapped([ctx = std::move(ctx)] (future<reader_permit::resource_units> f) mutable {
                        f.ignore_ready_future();
                        ctx->permit.reset(); // make sure it's destroyed before the semaphore
                    });
                }
                return make_ready_future<>();
            });
        }).finally([zis = shared_from_this()] {});
    }
    virtual reader_concurrency_semaphore& semaphore() override {
        const auto shard = this_shard_id();
        if (!_contexts[shard]) {
            _contexts[shard] = make_foreign(std::make_unique<reader_context>());
        } else if (_contexts[shard]->semaphore) {
            return *_contexts[shard]->semaphore;
        }
        if (_evict_paused_readers) {
            _contexts[shard]->semaphore = std::make_unique<reader_concurrency_semaphore>(0, std::numeric_limits<ssize_t>::max(),
                    format("reader_concurrency_semaphore @shard_id={}", shard));
            _contexts[shard]->permit = _contexts[shard]->semaphore->make_permit();
            // Add a waiter, so that all registered inactive reads are
            // immediately evicted.
            // We don't care about the returned future.
            _contexts[shard]->wait_future = _contexts[shard]->permit->wait_admission(1, db::no_timeout);
        } else {
            _contexts[shard]->semaphore = std::make_unique<reader_concurrency_semaphore>(reader_concurrency_semaphore::no_limits{});
        }
        return *_contexts[shard]->semaphore;
    }
};

<|endoftext|>


<commit_before>#include "gtest/gtest.h"

#include <node.h>
#include <parser.h>
#include <misc/fatal.h>

using namespace ydsh;

static std::vector<std::string> tokenize(const char *str) {
    std::vector<std::string> tokens;
    std::string tokenBuf;

    for(unsigned int i = 0; str[i] != '\0'; i++) {
        char ch = str[i];
        switch(ch) {
        case '(':
        case ')': {
            if(!tokenBuf.empty()) {
                tokens.push_back(std::move(tokenBuf));
            }

            tokenBuf += ch;
            tokens.push_back(std::move(tokenBuf));
            break;
        }
        case ' ':
        case '\t': {
            if(!tokenBuf.empty()) {
                tokens.push_back(std::move(tokenBuf));
            }
            break;
        }
        default:
            tokenBuf += ch;
            break;
        }
    }
    if(!tokenBuf.empty()) {
        tokens.push_back(std::move(tokenBuf));
    }

    return tokens;
}

class PrettyPrinter : public BaseVisitor {
private:
    std::vector<std::string> out;

public:
    PrettyPrinter() = default;
    ~PrettyPrinter() = default;

    std::vector<std::string> operator()(RootNode &rootNode) {
        this->visitRootNode(rootNode);
        return std::move(this->out);
    }

    void append(const char *str) {
        this->out.push_back(str);
    }

    void append(const std::string &str) {
        this->out.push_back(str);
    }

    void open() {
        this->append("(");
    }

    void close() {
        this->append(")");
    }

    void visitDefault(Node &) override {
        fatal("unsupported\n");
    }

    void visitNumberNode(NumberNode &node) override {
        this->append(std::to_string(node.getIntValue()));
    }

    void visitTypeOpNode(TypeOpNode &node) override {
        this->open();
        this->visit(*node.getExprNode());
        this->append(node.isCastOp() ? "as" : "is");
        this->append(dynamic_cast<BaseTypeNode *>(node.getTargetTypeNode())->getTokenText()); //FIXME:
        this->close();
    }

    void visitBinaryOpNode(BinaryOpNode &node) override {
        this->open();
        this->visit(*node.getLeftNode());
        this->append(TO_NAME(node.getOp()));
        this->visit(*node.getRightNode());
        this->close();
    }

    void visitIfNode(IfNode &node) override {
        this->open();
        this->visit(*node.getCondNode());
        this->append("?");
        this->visit(*node.getThenNode());
        this->append(":");
        this->visit(*node.getElseNode());
        this->close();
    }

    void visitAssignNode(AssignNode &node) override {
        this->open();
        this->visit(*node.getLeftNode());
        this->append("=");
        this->visit(*node.getRightNode());
        this->close();
    }

    void visitRootNode(RootNode &node) override {
        if(node.getNodes().size() != 1) {
            fatal("must be 1\n");
        }

        this->visit(*node.getNodes().front());
    }
};

class PrecedenceTest : public ::testing::Test {
public:
    PrecedenceTest() = default;
    virtual ~PrecedenceTest() = default;

    virtual void SetUp() { }

    virtual void TearDown() { }

    virtual void equalsTokens(const std::vector<std::string> &expected, const std::vector<std::string> &actual) {
        SCOPED_TRACE("");

        // check size
        unsigned int size = expected.size();
        ASSERT_EQ(size, actual.size());

        // check each
        for(unsigned int i = 0; i < size; i++) {
            ASSERT_EQ(expected[i], actual[i]);
        }
    }

    virtual void equals(const char *expected, const char *input) {
        SCOPED_TRACE("");

        ASSERT_TRUE(expected != nullptr);
        ASSERT_TRUE(input != nullptr);

        // parse
        Lexer lexer("(string)", input);
        Parser parser(lexer);
        auto rootNode = parser();
        ASSERT_FALSE(parser.hasError());

        std::vector<std::string> actualTokens = PrettyPrinter()(*rootNode);
        std::vector<std::string> expectedTokens = tokenize(expected);

        this->equalsTokens(expectedTokens, actualTokens);
    }
};

TEST_F(PrecedenceTest, base1) {
    ASSERT_NO_FATAL_FAILURE(this->equals("1", "1"));
}

TEST_F(PrecedenceTest, base2) {
    ASSERT_NO_FATAL_FAILURE(this->equals("  1  ", "(1)"));
}

TEST_F(PrecedenceTest, base3) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(1 + 2)", "1+2"));
}

TEST_F(PrecedenceTest, case1) {
    ASSERT_NO_FATAL_FAILURE(this->equals("((1 as Int) is Int)", "1 as Int is Int"));
}

TEST_F(PrecedenceTest, case2) {
    ASSERT_NO_FATAL_FAILURE(this->equals("((1 is Int) as Int)", "1 is Int as Int"));
}

TEST_F(PrecedenceTest, case3) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(12 / (23 as Int))", "12 / 23 as Int"));
}

TEST_F(PrecedenceTest, case4) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(((1 / 2) * 3) % 4)", "1 / 2 * 3 % 4"));
}

TEST_F(PrecedenceTest, case5) {
    ASSERT_NO_FATAL_FAILURE(this->equals("((1 + (2 * 3)) - 4)", "1 + 2 * 3 - 4"));
}

TEST_F(PrecedenceTest, case6) {
    ASSERT_NO_FATAL_FAILURE(this->equals("((1 and 2) or (3 xor (4 + 3)))", "1 and 2 or 3 xor 4 + 3"));
}

TEST_F(PrecedenceTest, case7) {
    ASSERT_NO_FATAL_FAILURE(
        this->equals("((((((((1 < 2) > 3) == 4) >= 5) !~ 6) <= 7) != 8) =~ 9)",
                     "1 < 2 > 3 == 4 >= 5 !~ 6 <= 7 != 8 =~ 9"));
}

TEST_F(PrecedenceTest, case8) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(1 = (((2 == 3) && (4 + 5)) || 6))", "1 = 2 == 3 && 4 + 5 || 6"));
}

TEST_F(PrecedenceTest, case9) {
    ASSERT_NO_FATAL_FAILURE(
            this->equals("((1 == 2) ? ((3 > 4) ? (5 + 6) : (7 xor 8)) : (9 && 10))",
                         "1 == 2 ? 3 > 4 ? 5 + 6 : 7 xor 8 : 9 && 10"));
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<commit_msg>update operator precedence test case, close #283<commit_after>#include "gtest/gtest.h"

#include <node.h>
#include <parser.h>
#include <misc/fatal.h>

using namespace ydsh;

static std::vector<std::string> tokenize(const char *str) {
    std::vector<std::string> tokens;
    std::string tokenBuf;

    for(unsigned int i = 0; str[i] != '\0'; i++) {
        char ch = str[i];
        switch(ch) {
        case '(':
        case ')': {
            if(!tokenBuf.empty()) {
                tokens.push_back(std::move(tokenBuf));
            }

            tokenBuf += ch;
            tokens.push_back(std::move(tokenBuf));
            break;
        }
        case ' ':
        case '\t': {
            if(!tokenBuf.empty()) {
                tokens.push_back(std::move(tokenBuf));
            }
            break;
        }
        default:
            tokenBuf += ch;
            break;
        }
    }
    if(!tokenBuf.empty()) {
        tokens.push_back(std::move(tokenBuf));
    }

    return tokens;
}

class PrettyPrinter : public BaseVisitor {
private:
    std::vector<std::string> out;

public:
    PrettyPrinter() = default;
    ~PrettyPrinter() = default;

    std::vector<std::string> operator()(RootNode &rootNode) {
        this->visitRootNode(rootNode);
        return std::move(this->out);
    }

    void append(const char *str) {
        this->out.push_back(str);
    }

    void append(const std::string &str) {
        this->out.push_back(str);
    }

    void open() {
        this->append("(");
    }

    void close() {
        this->append(")");
    }

    void visitDefault(Node &) override {
        fatal("unsupported\n");
    }

    void visitNumberNode(NumberNode &node) override {
        this->append(std::to_string(node.getIntValue()));
    }

    void visitTypeOpNode(TypeOpNode &node) override {
        this->open();
        this->visit(*node.getExprNode());
        this->append(node.isCastOp() ? "as" : "is");
        this->append(dynamic_cast<BaseTypeNode *>(node.getTargetTypeNode())->getTokenText()); //FIXME:
        this->close();
    }

    void visitBinaryOpNode(BinaryOpNode &node) override {
        this->open();
        this->visit(*node.getLeftNode());
        this->append(TO_NAME(node.getOp()));
        this->visit(*node.getRightNode());
        this->close();
    }

    void visitIfNode(IfNode &node) override {
        this->open();
        this->visit(*node.getCondNode());
        this->append("?");
        this->visit(*node.getThenNode());
        this->append(":");
        this->visit(*node.getElseNode());
        this->close();
    }

    void visitAssignNode(AssignNode &node) override {
        this->open();
        this->visit(*node.getLeftNode());
        this->append("=");
        this->visit(*node.getRightNode());
        this->close();
    }

    void visitJumpNode(JumpNode &node) override {
        this->open();
        assert(node.getOpKind() == JumpNode::THROW);
        this->append("throw");
        this->visit(*node.getExprNode());
        this->close();
    }

    void visitWithNode(WithNode &node) override {
        this->open();
        this->visit(*node.getExprNode());
        this->append("with");
        this->visit(*node.getRedirNodes().back());
        this->close();
    }

    void visitRedirNode(RedirNode &node) override {
        this->append(TO_NAME(node.getRedirectOP()));
        this->visit(*node.getTargetNode());
    }

    void visitCmdArgNode(CmdArgNode &node) override {
        this->visit(*node.getSegmentNodes().back());
    }

    void visitStringNode(StringNode &node) override {
        this->append(node.getValue());
    }

    void visitPipelineNode(PipelineNode &node) override {
        this->open();
        for(unsigned int i = 0; i < node.getNodes().size(); i++) {
            if(i > 0) {
                this->append("|");
            }
            this->visit(*node.getNodes()[i]);
        }
        this->close();
    }

    void visitRootNode(RootNode &node) override {
        if(node.getNodes().size() != 1) {
            fatal("must be 1\n");
        }

        this->visit(*node.getNodes().front());
    }
};

class PrecedenceTest : public ::testing::Test {
public:
    PrecedenceTest() = default;
    virtual ~PrecedenceTest() = default;

    virtual void SetUp() { }

    virtual void TearDown() { }

    virtual void equalsTokens(const std::vector<std::string> &expected, const std::vector<std::string> &actual) {
        SCOPED_TRACE("");

        // check size
        unsigned int size = expected.size();
        ASSERT_EQ(size, actual.size());

        // check each
        for(unsigned int i = 0; i < size; i++) {
            ASSERT_EQ(expected[i], actual[i]);
        }
    }

    virtual void equals(const char *expected, const char *input) {
        SCOPED_TRACE("");

        ASSERT_TRUE(expected != nullptr);
        ASSERT_TRUE(input != nullptr);

        // parse
        Lexer lexer("(string)", input);
        Parser parser(lexer);
        auto rootNode = parser();
        ASSERT_FALSE(parser.hasError());

        std::vector<std::string> actualTokens = PrettyPrinter()(*rootNode);
        std::vector<std::string> expectedTokens = tokenize(expected);

        this->equalsTokens(expectedTokens, actualTokens);
    }
};

TEST_F(PrecedenceTest, base1) {
    ASSERT_NO_FATAL_FAILURE(this->equals("1", "1"));
}

TEST_F(PrecedenceTest, base2) {
    ASSERT_NO_FATAL_FAILURE(this->equals("  1  ", "(1)"));
}

TEST_F(PrecedenceTest, base3) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(1 + 2)", "1+2"));
}

TEST_F(PrecedenceTest, case1) {
    ASSERT_NO_FATAL_FAILURE(this->equals("((1 as Int) is Int)", "1 as Int is Int"));
}

TEST_F(PrecedenceTest, case2) {
    ASSERT_NO_FATAL_FAILURE(this->equals("((1 is Int) as Int)", "1 is Int as Int"));
}

TEST_F(PrecedenceTest, case3) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(12 / (23 as Int))", "12 / 23 as Int"));
}

TEST_F(PrecedenceTest, case4) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(((1 / 2) * 3) % 4)", "1 / 2 * 3 % 4"));
}

TEST_F(PrecedenceTest, case5) {
    ASSERT_NO_FATAL_FAILURE(this->equals("((1 + (2 * 3)) - 4)", "1 + 2 * 3 - 4"));
}

TEST_F(PrecedenceTest, case6) {
    ASSERT_NO_FATAL_FAILURE(this->equals("((1 and 2) or (3 xor (4 + 3)))", "1 and 2 or 3 xor 4 + 3"));
}

TEST_F(PrecedenceTest, case7) {
    ASSERT_NO_FATAL_FAILURE(
        this->equals("((((((((1 < 2) > 3) == 4) >= 5) !~ 6) <= 7) != 8) =~ 9)",
                     "1 < 2 > 3 == 4 >= 5 !~ 6 <= 7 != 8 =~ 9"));
}

TEST_F(PrecedenceTest, case8) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(1 = (((2 == 3) && (4 + 5)) || 6))", "1 = 2 == 3 && 4 + 5 || 6"));
}

TEST_F(PrecedenceTest, case9) {
    ASSERT_NO_FATAL_FAILURE(
            this->equals("((1 == 2) ? ((3 > 4) ? (5 + 6) : (7 xor 8)) : (9 && 10))",
                         "1 == 2 ? 3 > 4 ? 5 + 6 : 7 xor 8 : 9 && 10"));
}

TEST_F(PrecedenceTest, case10) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(55 < ((65 or 75) ?? 78))", "55 < 65 or 75 ?? 78"));
}

TEST_F(PrecedenceTest, case11) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(((throw 45) + 45) && 54)", "throw 45 + 45 && 54"));
}

TEST_F(PrecedenceTest, case12) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(34 || ((45 | (56 and 67)) && 78))", "34 || 45 | 56 and 67 && 78"));
}

TEST_F(PrecedenceTest, case13) {
    ASSERT_NO_FATAL_FAILURE(this->equals("(45 | ((56 as Int) with 2> 67))", "45 | 56 as Int with 2> 67"));
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>#include <tamer/tamer.hh>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
namespace tamer {

volatile sig_atomic_t driver::sig_any_active;
int driver::sig_pipe[2] = { -1, -1 };

namespace {

volatile sig_atomic_t sig_active[NSIG];
volatile int sig_installing = -1;
event<> sig_handlers[NSIG];

class sigcancel_rendezvous : public rendezvous<> { public:

    sigcancel_rendezvous() {
    }

    inline void add(tamerpriv::simple_event *e) throw () {
	_nwaiting++;
	e->initialize(this, sig_installing);
    }

    void complete(uintptr_t rid) {
	if ((int) rid != sig_installing) {
	    struct sigaction sa;
	    sa.sa_handler = SIG_DFL;
	    sigemptyset(&sa.sa_mask);
	    sa.sa_flags = SA_RESETHAND;
	    sigaction(rid, &sa, 0);
	}
	rendezvous<>::complete(rid);
    }

};

sigcancel_rendezvous sigcancelr;

}

extern "C" {
static void tame_signal_handler(int signal) {
    int save_errno = errno;

    driver::sig_any_active = sig_active[signal] = 1;

    // ensure select wakes up
    write(driver::sig_pipe[1], "", 1);

    // block signal until we trigger the event, giving the unblocked
    // rendezvous a chance to maybe install a different handler
    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset, signal);
    sigprocmask(SIG_BLOCK, &sigset, NULL);

    errno = save_errno;
}
}

void driver::at_signal(int signal, const event<> &trigger)
{
    assert(signal >= 0 && signal < NSIG);

    if (sig_pipe[0] < 0) {
	pipe(sig_pipe);
	fcntl(sig_pipe[0], F_SETFL, O_NONBLOCK);
	fcntl(sig_pipe[0], F_SETFD, FD_CLOEXEC);
	fcntl(sig_pipe[1], F_SETFD, FD_CLOEXEC);
    }

    if (!trigger)		// special case forces creation of signal pipe
	return;

    if (sig_handlers[signal])
	sig_handlers[signal] = distribute(sig_handlers[signal], trigger);
    else {
	sig_installing = signal;

	sig_handlers[signal] = trigger;
	sig_handlers[signal].at_trigger(make_event(sigcancelr));

	struct sigaction sa;
	sa.sa_handler = tame_signal_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_RESETHAND;
	sigaction(signal, &sa, 0);

	sig_installing = -1;
    }
}

void driver::dispatch_signals()
{
    sig_any_active = 0;
    sigset_t sigs_unblock;
    sigemptyset(&sigs_unblock);

    // kill crap data written to pipe
    char crap[64];
    while (read(sig_pipe[0], crap, 64) > 0)
	/* do nothing */;

    // check signals
    for (int sig = 0; sig < NSIG; sig++)
	if (sig_active[sig]) {
	    sig_active[sig] = 0;
	    sig_handlers[sig].trigger();
	    sigaddset(&sigs_unblock, sig);
	}

    // run closures activated by signals (plus maybe some others)
    while (tamerpriv::abstract_rendezvous *r = tamerpriv::abstract_rendezvous::unblocked)
	r->run();

    // now that the signal responders have potentially reinstalled signal
    // handlers, unblock the signals
    sigprocmask(SIG_UNBLOCK, &sigs_unblock, 0);
}

}
<commit_msg>a bit of signal nonsense, should test<commit_after>#include <tamer/tamer.hh>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
namespace tamer {

volatile sig_atomic_t driver::sig_any_active;
int driver::sig_pipe[2] = { -1, -1 };

namespace {

volatile sig_atomic_t sig_active[NSIG];
volatile int sig_installing = -1;
event<> sig_handlers[NSIG];

class sigcancel_rendezvous : public rendezvous<> { public:

    sigcancel_rendezvous() {
    }

    inline void add(tamerpriv::simple_event *e) throw () {
	_nwaiting++;
	e->initialize(this, sig_installing);
    }

    void complete(uintptr_t rid) {
	if ((int) rid != sig_installing) {
	    struct sigaction sa;
	    sa.sa_handler = SIG_DFL;
	    sigemptyset(&sa.sa_mask);
	    sa.sa_flags = SA_RESETHAND;
	    sigaction(rid, &sa, 0);
	}
	rendezvous<>::complete(rid);
    }

};

sigcancel_rendezvous sigcancelr;

}

extern "C" {
static void tame_signal_handler(int signal) {
    int save_errno = errno;

    driver::sig_any_active = sig_active[signal] = 1;

    // ensure select wakes up
    write(driver::sig_pipe[1], "", 1);

    // block signal until we trigger the event, giving the unblocked
    // rendezvous a chance to maybe install a different handler
    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset, signal);
    sigprocmask(SIG_BLOCK, &sigset, NULL);

    errno = save_errno;
}
}

void driver::at_signal(int signal, const event<> &trigger)
{
    assert(signal >= 0 && signal < NSIG);

    if (sig_pipe[0] < 0) {
	pipe(sig_pipe);
	fcntl(sig_pipe[0], F_SETFL, O_NONBLOCK);
	fcntl(sig_pipe[0], F_SETFD, FD_CLOEXEC);
	fcntl(sig_pipe[1], F_SETFD, FD_CLOEXEC);
    }

    if (!trigger)		// special case forces creation of signal pipe
	return;

    if (sig_handlers[signal])
	sig_handlers[signal] = distribute(sig_handlers[signal], trigger);
    else {
	int old_sig_installing = sig_installing;
	sig_installing = signal;

	sig_handlers[signal] = trigger;
	sig_handlers[signal].at_trigger(make_event(sigcancelr));

	struct sigaction sa;
	sa.sa_handler = tame_signal_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_RESETHAND;
	sigaction(signal, &sa, 0);

	sig_installing = old_sig_installing;
    }
}

void driver::dispatch_signals()
{
    sig_any_active = 0;
    sigset_t sigs_unblock;
    sigemptyset(&sigs_unblock);

    // kill crap data written to pipe
    char crap[64];
    while (read(sig_pipe[0], crap, 64) > 0)
	/* do nothing */;

    // check signals
    for (int sig = 0; sig < NSIG; sig++)
	if (sig_active[sig]) {
	    sig_active[sig] = 0;
	    sig_installing = sig;
	    sig_handlers[sig].trigger();
	    sig_installing = -1;
	    sigaddset(&sigs_unblock, sig);
	}

    // run closures activated by signals (plus maybe some others)
    while (tamerpriv::abstract_rendezvous *r = tamerpriv::abstract_rendezvous::unblocked)
	r->run();

    // now that the signal responders have potentially reinstalled signal
    // handlers, unblock the signals
    sigprocmask(SIG_UNBLOCK, &sigs_unblock, 0);
}

}
<|endoftext|>


<commit_before>#ifndef UTIL_HH
#define UTIL_HH

#include <algorithm>
#include <vector>
#include <math.h>

/** Common utility functions. */
namespace util {

  /** The square of the value. */
  template <typename T>
  T sqr(T a)
  {
    return a * a;
  }

  /** Median of the values in a vector.
   * \note For odd number (2n + 1) of values, the n'th value is returned.
   */
  template <typename T>
  T
  median(std::vector<T> v)
  {
    std::sort(v.begin(), v.end());
    return v[v.size() / 2];
  }

  /** Absolute value. */
  template <typename T>
  T
  abs(const T &value)
  {
    if (value < 0)
      return -value;
    return value;
  }

  /** Maximum of two values. */
  template <typename T>
  T
  max(const T &a, const T &b)
  {
    if (a < b)
      return b;
    return a;
  }

  inline float
  log10add(float a, float b)
  {
    const float LOG10TOe = M_LN10;
    const float LOGeTO10 = 1.0 / M_LN10;

    a = a * LOG10TOe;
    b = b * LOG10TOe;

    float delta = a - b;
    if (delta > 64.0) {
      b += 64;
      delta = -delta;
    }
    return (b + log1pf(exp(delta))) * LOGeTO10;
  }

  inline float
  logadd(float a, float b)
  {
    float delta = a - b;
    if (delta > 64.0) {
      b += 64;
      delta = -delta;
    }
    return b + log1pf(exp(delta));
  }

  static const float tiny_for_log = (float)1e-16;
  inline double safe_log(double x)
  {
    if (x < tiny_for_log)
      return logf(tiny_for_log);
    else
      return logf(x);
  }

};

#endif /* UTIL_HH */
<commit_msg>added modulo<commit_after>#ifndef UTIL_HH
#define UTIL_HH

#include <algorithm>
#include <vector>
#include <math.h>

/** Common utility functions. */
namespace util {

  /** The square of the value. */
  template <typename T>
  T sqr(T a)
  {
    return a * a;
  }

  /** Median of the values in a vector.
   * \note For odd number (2n + 1) of values, the n'th value is returned.
   */
  template <typename T>
  T
  median(std::vector<T> v)
  {
    std::sort(v.begin(), v.end());
    return v[v.size() / 2];
  }

  /** Absolute value. */
  template <typename T>
  T
  abs(const T &value)
  {
    if (value < 0)
      return -value;
    return value;
  }

  /** Maximum of two values. */
  template <typename T>
  T
  max(const T &a, const T &b)
  {
    if (a < b)
      return b;
    return a;
  }

  inline float
  log10add(float a, float b)
  {
    const float LOG10TOe = M_LN10;
    const float LOGeTO10 = 1.0 / M_LN10;

    a = a * LOG10TOe;
    b = b * LOG10TOe;

    float delta = a - b;
    if (delta > 64.0) {
      b += 64;
      delta = -delta;
    }
    return (b + log1pf(exp(delta))) * LOGeTO10;
  }

  inline float
  logadd(float a, float b)
  {
    float delta = a - b;
    if (delta > 64.0) {
      b += 64;
      delta = -delta;
    }
    return b + log1pf(exp(delta));
  }

  static const float tiny_for_log = (float)1e-16;
  inline double safe_log(double x)
  {
    if (x < tiny_for_log)
      return logf(tiny_for_log);
    else
      return logf(x);
  }

  /** Compute modulo of two values so that negative arguments are
   * handled correctly. */
  int modulo(int a, int b)
  {
    int result = a % b;
    if (result < 0)
      result += b;
    return result;
  }

};

#endif /* UTIL_HH */
<|endoftext|>


<commit_before>// This file is part of the dune-stuff project:
//   https://users.dune-project.org/projects/dune-stuff/
// Copyright Holders: Felix Albrecht, Rene Milk
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#warning This header is deprecated, use '#include <dune/stuff/la/container/common.hh' instead!
#include "common.hh"
<commit_msg>[la.container.dunedynamic] fixed deprecation warning<commit_after>// This file is part of the dune-stuff project:
//   https://users.dune-project.org/projects/dune-stuff/
// Copyright Holders: Felix Albrecht, Rene Milk
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#warning This header is deprecated, use '#include <dune/stuff/la/container/common.hh>' instead!
#include "common.hh"
<|endoftext|>


<commit_before>#define EXT_SINGULAR 2
#define EXT_DEPTH_CHECK 3
#define EXT_DEPTH_ONE_REPLY 2
#define EXT_DEPTH_RECAP 2
#define NULL_DEPTH_RATE 12
#define NULL_DEPTH_REDUCE 14
#define NULL_DEPTH_VRATE 510
#define REDUCTION_RATE1 12
#define REDUCTION_RATE2 12
#define RAZOR_MARGIN1 230
#define RAZOR_MARGIN2 700
#define RAZOR_MARGIN3 820
#define RAZOR_MARGIN4 660
#define FUT_PRUN_MAX_DEPTH 24
#define FUT_PRUN_MARGIN_RATE 40
#define FUT_PRUN_MARGIN 70
#define PROBCUT_MARGIN 200
#define PROBCUT_REDUCTION 16
#define ASP_MIN_DEPTH 0
#define ASP_1ST_DELTA 70
#define ASP_DELTA_RATE 20
#define SINGULAR_DEPTH 36
#define SINGULAR_MARGIN 20
<commit_msg>Fix ASP_MIN_DEPTH parameter<commit_after>#define EXT_SINGULAR 2
#define EXT_DEPTH_CHECK 3
#define EXT_DEPTH_ONE_REPLY 2
#define EXT_DEPTH_RECAP 2
#define NULL_DEPTH_RATE 12
#define NULL_DEPTH_REDUCE 14
#define NULL_DEPTH_VRATE 510
#define REDUCTION_RATE1 12
#define REDUCTION_RATE2 12
#define RAZOR_MARGIN1 230
#define RAZOR_MARGIN2 700
#define RAZOR_MARGIN3 820
#define RAZOR_MARGIN4 660
#define FUT_PRUN_MAX_DEPTH 24
#define FUT_PRUN_MARGIN_RATE 40
#define FUT_PRUN_MARGIN 70
#define PROBCUT_MARGIN 200
#define PROBCUT_REDUCTION 16
#define ASP_MIN_DEPTH 2
#define ASP_1ST_DELTA 70
#define ASP_DELTA_RATE 20
#define SINGULAR_DEPTH 36
#define SINGULAR_MARGIN 20
<|endoftext|>


<commit_before>// Copyright 2014-2015 Project Vogue. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <sstream>
#include <string>

#include "elang/compiler/testing/analyzer_test.h"

#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "elang/compiler/compilation_session.h"
#include "elang/compiler/predefined_names.h"
#include "elang/compiler/semantics/editor.h"
#include "elang/compiler/semantics/factory.h"
#include "elang/compiler/semantics/nodes.h"
#include "elang/compiler/source_code_range.h"
#include "elang/compiler/token.h"
#include "elang/compiler/token_type.h"

namespace elang {
namespace compiler {
namespace sm {

//////////////////////////////////////////////////////////////////////
//
// IrSemanticsTest
//
class IrSemanticsTest : public testing::AnalyzerTest {
 protected:
  IrSemanticsTest();
  ~IrSemanticsTest() override = default;

  Editor* editor() { return &editor_; }
  Factory* factory() { return editor()->factory(); }
  Type* system_int32();
  Type* system_int64();

  Value* NewLiteral(Type* type, const TokenData& data);
  Token* NewToken(const TokenData& data);
  Token* NewToken(base::StringPiece name);
  std::string ToString(Semantic* node);

 private:
  sm::Editor editor_;

  DISALLOW_COPY_AND_ASSIGN(IrSemanticsTest);
};

IrSemanticsTest::IrSemanticsTest() : editor_(session()) {
}

Type* IrSemanticsTest::system_int32() {
  return session()->PredefinedTypeOf(PredefinedName::Int32);
}

Type* IrSemanticsTest::system_int64() {
  return session()->PredefinedTypeOf(PredefinedName::Int64);
}

Value* IrSemanticsTest::NewLiteral(Type* type, const TokenData& data) {
  return factory()->NewLiteral(type, NewToken(data));
}

Token* IrSemanticsTest::NewToken(const TokenData& data) {
  return session()->NewToken(SourceCodeRange(), data);
}

Token* IrSemanticsTest::NewToken(base::StringPiece name) {
  return NewToken(
      TokenData(session()->NewAtomicString(base::UTF8ToUTF16(name))));
}

std::string IrSemanticsTest::ToString(Semantic* semantic) {
  std::stringstream ostream;
  ostream << *semantic;
  return ostream.str();
}

TEST_F(IrSemanticsTest, ArrayType) {
  auto const type1 = factory()->NewArrayType(system_int32(), {10, 20});
  auto const type2 = factory()->NewArrayType(system_int32(), {10, 20});
  auto const type3 = factory()->NewArrayType(system_int32(), {10});
  EXPECT_EQ(type1, type2)
      << "array type should be unique by element type and dimensions";
  EXPECT_NE(type1, type3);
  EXPECT_EQ(2, type1->rank());
  EXPECT_EQ("System.Int32[10, 20]", ToString(type1));
  EXPECT_EQ("System.Int32[10]", ToString(type3));
}

// Element type of array type is omitting left most rank, e.g.
//  element_type_of(T[A]) = T
//  element_type_of(T[A][B}) = T[B]
//  element_type_of(T[A][B}[C]) = T[B][C]
TEST_F(IrSemanticsTest, ArrayTypeArrayOfArray) {
  auto const type1 = factory()->NewArrayType(system_int32(), {10});
  auto const type2 = factory()->NewArrayType(type1, {20});
  auto const type3 = factory()->NewArrayType(type2, {30});
  EXPECT_EQ("System.Int32[10]", ToString(type1));
  EXPECT_EQ("System.Int32[20][10]", ToString(type2));
  EXPECT_EQ("System.Int32[30][20][10]", ToString(type3));
}

TEST_F(IrSemanticsTest, ArrayTypeUnbound) {
  EXPECT_EQ("System.Int32[]",
            ToString(factory()->NewArrayType(system_int32(), {-1})));
  EXPECT_EQ("System.Int32[,]",
            ToString(factory()->NewArrayType(system_int32(), {-1, -1})));
  EXPECT_EQ("System.Int32[,,]",
            ToString(factory()->NewArrayType(system_int32(), {-1, -1, -1})));
}

TEST_F(IrSemanticsTest, Enum) {
  auto const outer =
      factory()->NewNamespace(factory()->global_namespace(), NewToken("Foo"));
  auto const enum_base = system_int64();
  auto const enum_type =
      factory()->NewEnum(outer, NewToken("Color"), enum_base);
  factory()->NewEnumMember(enum_type, NewToken("Red"), nullptr);
  factory()->NewEnumMember(enum_type, NewToken("Green"), nullptr);
  factory()->NewEnumMember(
      enum_type, NewToken("Blue"),
      NewLiteral(enum_base, TokenData(TokenType::Int32Literal, 42)));
  EXPECT_EQ("enum Foo.Color : System.Int64 {Red, Green, Blue = 42}",
            ToString(enum_type));
}

TEST_F(IrSemanticsTest, Namespace) {
  auto const ns1 =
      factory()->NewNamespace(factory()->global_namespace(), NewToken("Foo"));
  auto const ns2 = factory()->NewNamespace(ns1, NewToken("Bar"));
  EXPECT_EQ("namespace Foo", ToString(ns1));
  EXPECT_EQ("namespace Foo.Bar", ToString(ns2));
}

}  // namespace sm
}  // namespace compiler
}  // namespace elang
<commit_msg>elang/compiler/semantics: Tidy up "node_test.cc".<commit_after>// Copyright 2014-2015 Project Vogue. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <sstream>
#include <string>

#include "elang/compiler/testing/analyzer_test.h"

#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "elang/compiler/compilation_session.h"
#include "elang/compiler/predefined_names.h"
#include "elang/compiler/semantics/editor.h"
#include "elang/compiler/semantics/factory.h"
#include "elang/compiler/semantics/nodes.h"
#include "elang/compiler/source_code_range.h"
#include "elang/compiler/token.h"
#include "elang/compiler/token_type.h"

namespace elang {
namespace compiler {
namespace sm {

//////////////////////////////////////////////////////////////////////
//
// SemanticTest
//
class SemanticTest : public testing::AnalyzerTest {
 protected:
  SemanticTest();
  ~SemanticTest() override = default;

  Editor* editor() { return &editor_; }
  Factory* factory() { return editor()->factory(); }
  Type* int32_type();
  Type* int64_type();

  Value* NewLiteral(Type* type, const TokenData& data);
  Token* NewToken(const TokenData& data);
  Token* NewToken(base::StringPiece name);
  std::string ToString(Semantic* node);

 private:
  sm::Editor editor_;

  DISALLOW_COPY_AND_ASSIGN(SemanticTest);
};

SemanticTest::SemanticTest() : editor_(session()) {
}

Type* SemanticTest::int32_type() {
  return session()->PredefinedTypeOf(PredefinedName::Int32);
}

Type* SemanticTest::int64_type() {
  return session()->PredefinedTypeOf(PredefinedName::Int64);
}

Value* SemanticTest::NewLiteral(Type* type, const TokenData& data) {
  return factory()->NewLiteral(type, NewToken(data));
}

Token* SemanticTest::NewToken(const TokenData& data) {
  return session()->NewToken(SourceCodeRange(), data);
}

Token* SemanticTest::NewToken(base::StringPiece name) {
  return NewToken(
      TokenData(session()->NewAtomicString(base::UTF8ToUTF16(name))));
}

std::string SemanticTest::ToString(Semantic* semantic) {
  std::stringstream ostream;
  ostream << *semantic;
  return ostream.str();
}

TEST_F(SemanticTest, ArrayType) {
  auto const type1 = factory()->NewArrayType(int32_type(), {10, 20});
  auto const type2 = factory()->NewArrayType(int32_type(), {10, 20});
  auto const type3 = factory()->NewArrayType(int32_type(), {10});
  EXPECT_EQ(type1, type2)
      << "array type should be unique by element type and dimensions";
  EXPECT_NE(type1, type3);
  EXPECT_EQ(2, type1->rank());
  EXPECT_EQ("System.Int32[10, 20]", ToString(type1));
  EXPECT_EQ("System.Int32[10]", ToString(type3));
}

// Element type of array type is omitting left most rank, e.g.
//  element_type_of(T[A]) = T
//  element_type_of(T[A][B}) = T[B]
//  element_type_of(T[A][B}[C]) = T[B][C]
TEST_F(SemanticTest, ArrayTypeArrayOfArray) {
  auto const type1 = factory()->NewArrayType(int32_type(), {10});
  auto const type2 = factory()->NewArrayType(type1, {20});
  auto const type3 = factory()->NewArrayType(type2, {30});
  EXPECT_EQ("System.Int32[10]", ToString(type1));
  EXPECT_EQ("System.Int32[20][10]", ToString(type2));
  EXPECT_EQ("System.Int32[30][20][10]", ToString(type3));
}

TEST_F(SemanticTest, ArrayTypeUnbound) {
  EXPECT_EQ("System.Int32[]",
            ToString(factory()->NewArrayType(int32_type(), {-1})));
  EXPECT_EQ("System.Int32[,]",
            ToString(factory()->NewArrayType(int32_type(), {-1, -1})));
  EXPECT_EQ("System.Int32[,,]",
            ToString(factory()->NewArrayType(int32_type(), {-1, -1, -1})));
}

TEST_F(SemanticTest, Enum) {
  auto const outer =
      factory()->NewNamespace(factory()->global_namespace(), NewToken("Foo"));
  auto const enum_base = int64_type();
  auto const enum_type =
      factory()->NewEnum(outer, NewToken("Color"), enum_base);
  factory()->NewEnumMember(enum_type, NewToken("Red"), nullptr);
  factory()->NewEnumMember(enum_type, NewToken("Green"), nullptr);
  factory()->NewEnumMember(
      enum_type, NewToken("Blue"),
      NewLiteral(enum_base, TokenData(TokenType::Int32Literal, 42)));
  EXPECT_EQ("enum Foo.Color : System.Int64 {Red, Green, Blue = 42}",
            ToString(enum_type));
}

TEST_F(SemanticTest, Namespace) {
  auto const ns1 =
      factory()->NewNamespace(factory()->global_namespace(), NewToken("Foo"));
  auto const ns2 = factory()->NewNamespace(ns1, NewToken("Bar"));
  EXPECT_EQ("namespace Foo", ToString(ns1));
  EXPECT_EQ("namespace Foo.Bar", ToString(ns2));
}

}  // namespace sm
}  // namespace compiler
}  // namespace elang
<|endoftext|>


<commit_before>#include <iostream>

int main()
{
    std::cout << "123\n";
}

<commit_msg>Add return 0;<commit_after>#include <iostream>

int main()
{
    std::cout << "123\n";
    return 0;
}

<|endoftext|>


<commit_before>#include "core/svg.h"

#include "core/numeric.h"
#include "core/angle.h"
#include "core/random.h"
#include "core/range.h"
#include "core/palette.h"
#include "core/palette_tableu.h"

using namespace euphoria::core;
using namespace euphoria::core::svg;

Poly
MakeStar(const vec2f& origo, float radius, const Rgbi& fill, const Angle& rotation)
{
    auto alpha = Angle::OneTurn() / 10;

    auto p = Poly{};

    for(auto i = 11; i > 0; i--)
    {
        auto r = radius*(i % 2 + 1)/2.0f;
        auto omega = rotation + alpha * static_cast<float>(i);
        p.points.push_back({(r * Sin(omega)) + origo.x, (r * Cos(omega)) + origo.y});
    }

    p.Close(fill);

    return p;
}

int
main(int, char*[])
{
    Random rand;

    auto pal = palette::Tableau_20();

    auto svg = Svg{};

    for(int i=0; i<30; i+=1)
    {
        const auto p = rand.PointOnUnitCircle_CenterFocused()*100.0f;
        const auto s = rand.Next(Range{5.0f, 30.0f});
        const auto c = pal.GetRandomColor(&rand);
        const auto r = Angle::FromPercentOf360(rand.NextFloat01());
        svg << MakeStar(p, s, c, r);
    }
    svg.AddAxis().Grid(10);
    svg.Write("stars.html");
}

<commit_msg>started using shufflebag instead of just random values<commit_after>#include "core/svg.h"

#include "core/numeric.h"
#include "core/angle.h"
#include "core/random.h"
#include "core/range.h"
#include "core/palette.h"
#include "core/palette_tableu.h"
#include "core/shufflebag.h"

using namespace euphoria::core;
using namespace euphoria::core::svg;

Poly
MakeStar(const vec2f& origo, float radius, const Rgbi& fill, const Angle& rotation)
{
    auto alpha = Angle::OneTurn() / 10;

    auto p = Poly{};

    for(auto i = 11; i > 0; i--)
    {
        auto r = radius*(i % 2 + 1)/2.0f;
        auto omega = rotation + alpha * static_cast<float>(i);
        p.points.push_back({(r * Sin(omega)) + origo.x, (r * Cos(omega)) + origo.y});
    }

    p.Close(fill);

    return p;
}

int
main(int, char*[])
{
    Random rand;

    auto pal = CreateShuffleBag(palette::ColorBlind_10().colors, 2);

    auto svg = Svg{};

    for(int i=0; i<30; i+=1)
    {
        const auto p = rand.PointOnUnitCircle_CenterFocused()*100.0f;
        const auto s = rand.Next(Range{5.0f, 30.0f});
        const auto c = pal.Next(&rand);
        const auto r = Angle::FromPercentOf360(rand.NextFloat01());
        svg << MakeStar(p, s, c, r);
    }
    svg.AddAxis().Grid(10);
    svg.Write("stars.html");
}

<|endoftext|>


<commit_before>#include "rtp/QualityManager.h"
#include "WebRtcConnection.h"

namespace erizo {

DEFINE_LOGGER(QualityManager, "rtp.QualityManager");

constexpr duration QualityManager::kMinLayerSwitchInterval;
constexpr duration QualityManager::kActiveLayerInterval;
constexpr float QualityManager::kIncreaseLayerBitrateThreshold;

QualityManager::QualityManager(std::shared_ptr<Clock> the_clock)
  : initialized_{false}, enabled_{false}, padding_enabled_{false}, forced_layers_{false},
  slideshow_mode_active_{false}, spatial_layer_{0}, temporal_layer_{0}, max_active_spatial_layer_{0},
  max_active_temporal_layer_{0}, current_estimated_bitrate_{0}, last_quality_check_{the_clock->now()},
  last_activity_check_{the_clock->now()}, clock_{the_clock} {}

void QualityManager::enable() {
  ELOG_DEBUG("message: Enabling QualityManager");
  enabled_ = true;
  setPadding(true);
}

void QualityManager::disable() {
  ELOG_DEBUG("message: Disabling QualityManager");
  enabled_ = false;
  setPadding(false);
}

void QualityManager::notifyQualityUpdate() {
  if (!enabled_) {
    return;
  }

  if (!initialized_) {
    auto pipeline = getContext()->getPipelineShared();
    if (!pipeline) {
      return;
    }
    stats_ = pipeline->getService<Stats>();
    if (!stats_) {
      return;
    }
    if (!stats_->getNode()["total"].hasChild("senderBitrateEstimation")) {
      return;
    }
    initialized_ = true;
  }

  if (forced_layers_) {
    return;
  }
  time_point now = clock_->now();
  current_estimated_bitrate_ = stats_->getNode()["total"]["senderBitrateEstimation"].value();
  uint64_t current_layer_instant_bitrate = getInstantLayerBitrate(spatial_layer_, temporal_layer_);
  bool estimated_is_under_layer_bitrate = current_estimated_bitrate_ < current_layer_instant_bitrate;

  if (now - last_activity_check_ > kActiveLayerInterval) {
    calculateMaxActiveLayer();
    last_activity_check_ = now;
  }

  bool layer_is_active = spatial_layer_ <= max_active_spatial_layer_;

  if (!layer_is_active || (estimated_is_under_layer_bitrate && !slideshow_mode_active_)) {
    ELOG_DEBUG("message: Forcing calculate new layer, "
        "estimated_is_under_layer_bitrate: %d, layer_is_active: %d", estimated_is_under_layer_bitrate,
        layer_is_active);
    selectLayer(false);
  } else if (now - last_quality_check_ > kMinLayerSwitchInterval) {
    selectLayer(true);
  }
}

void QualityManager::selectLayer(bool try_higher_layers) {
  last_quality_check_ = clock_->now();
  int aux_temporal_layer = 0;
  int aux_spatial_layer = 0;
  int next_temporal_layer = 0;
  int next_spatial_layer = 0;
  float bitrate_margin = try_higher_layers ? kIncreaseLayerBitrateThreshold : 0;
  bool below_min_layer = true;
  ELOG_DEBUG("Calculate best layer with %lu, current layer %d/%d",
      current_estimated_bitrate_, spatial_layer_, temporal_layer_);
  for (auto &spatial_layer_node : stats_->getNode()["qualityLayers"].getMap()) {
    for (auto &temporal_layer_node : stats_->getNode()["qualityLayers"][spatial_layer_node.first.c_str()].getMap()) {
      ELOG_DEBUG("Bitrate for layer %d/%d %lu",
          aux_spatial_layer, aux_temporal_layer, temporal_layer_node.second->value());
      if (temporal_layer_node.second->value() != 0 &&
          (1. + bitrate_margin) * temporal_layer_node.second->value() < current_estimated_bitrate_) {
        next_temporal_layer = aux_temporal_layer;
        next_spatial_layer = aux_spatial_layer;
        below_min_layer = false;
      }
      aux_temporal_layer++;
    }
    aux_temporal_layer = 0;
    aux_spatial_layer++;
  }

  if (below_min_layer != slideshow_mode_active_) {
    if (below_min_layer || try_higher_layers) {
      slideshow_mode_active_ = below_min_layer;
      ELOG_DEBUG("Slideshow fallback mode %d", slideshow_mode_active_);
      WebRtcConnection *connection = getContext()->getPipelineShared()->getService<WebRtcConnection>().get();
      if (connection) {
        connection->notifyUpdateToHandlers();
      }
    }
  }

  if (next_temporal_layer != temporal_layer_ || next_spatial_layer != spatial_layer_) {
    ELOG_DEBUG("message: Layer Switch, current_layer: %d/%d, new_layer: %d/%d",
        spatial_layer_, temporal_layer_, next_spatial_layer, next_temporal_layer);
    setTemporalLayer(next_temporal_layer);
    setSpatialLayer(next_spatial_layer);

    // TODO(javier): should we wait for the actual spatial switch?
    // should we disable padding temporarily to avoid congestion (old padding + new bitrate)?
    setPadding(!isInMaxLayer());
    ELOG_DEBUG("message: Is padding enabled, padding_enabled_: %d", padding_enabled_);
  }
}

void QualityManager::calculateMaxActiveLayer() {
  int max_active_spatial_layer = 5;
  int max_active_temporal_layer = 5;

  for (; max_active_spatial_layer > 0; max_active_spatial_layer--) {
    if (getInstantLayerBitrate(max_active_spatial_layer, 0) > 0) {
      break;
    }
  }
  for (; max_active_temporal_layer > 0; max_active_temporal_layer--) {
    if (getInstantLayerBitrate(max_active_spatial_layer, max_active_temporal_layer) > 0) {
      break;
    }
  }
  stats_->getNode()["qualityLayers"].insertStat("maxActiveSpatialLayer",
      CumulativeStat{static_cast<uint64_t>(max_active_spatial_layer_)});
  stats_->getNode()["qualityLayers"].insertStat("maxActiveTemporalLayer",
      CumulativeStat{static_cast<uint64_t>(max_active_temporal_layer_)});

  max_active_spatial_layer_ = max_active_spatial_layer;
  max_active_temporal_layer_ = max_active_temporal_layer;
}

uint64_t QualityManager::getInstantLayerBitrate(int spatial_layer, int temporal_layer) {
  if (!stats_->getNode()["qualityLayers"].hasChild(spatial_layer) ||
      !stats_->getNode()["qualityLayers"][spatial_layer].hasChild(temporal_layer)) {
    return 0;
  }

  MovingIntervalRateStat* layer_stat =
    reinterpret_cast<MovingIntervalRateStat*>(&stats_->getNode()["qualityLayers"][spatial_layer][temporal_layer]);
  return layer_stat->value(kActiveLayerInterval);
}

bool QualityManager::isInBaseLayer() {
  return (spatial_layer_ == 0 && temporal_layer_ == 0);
}

bool QualityManager::isInMaxLayer() {
  return (spatial_layer_ >= max_active_spatial_layer_ && temporal_layer_ >= max_active_temporal_layer_);
}

void QualityManager::forceLayers(int spatial_layer, int temporal_layer) {
  if (spatial_layer == -1 || temporal_layer == -1) {
    forced_layers_ = false;
    return;
  }
  forced_layers_ = true;
  setPadding(false);

  spatial_layer_ = spatial_layer;
  temporal_layer_ = temporal_layer;
}

void QualityManager::setSpatialLayer(int spatial_layer) {
  if (!forced_layers_) {
    spatial_layer_ = spatial_layer;
  }
  stats_->getNode()["qualityLayers"].insertStat("selectedSpatialLayer",
      CumulativeStat{static_cast<uint64_t>(spatial_layer_)});
}
void QualityManager::setTemporalLayer(int temporal_layer) {
  if (!forced_layers_) {
    temporal_layer_ = temporal_layer;
  }
  stats_->getNode()["qualityLayers"].insertStat("selectedTemporalLayer",
      CumulativeStat{static_cast<uint64_t>(temporal_layer_)});
}

void QualityManager::setPadding(bool enabled) {
  if (padding_enabled_ != enabled) {
    padding_enabled_ = enabled;
    getContext()->getPipelineShared()->getService<WebRtcConnection>()->notifyUpdateToHandlers();
  }
}

}  // namespace erizo
<commit_msg>Fix an issue when enabling padding the first time (#950)<commit_after>#include "rtp/QualityManager.h"
#include "WebRtcConnection.h"

namespace erizo {

DEFINE_LOGGER(QualityManager, "rtp.QualityManager");

constexpr duration QualityManager::kMinLayerSwitchInterval;
constexpr duration QualityManager::kActiveLayerInterval;
constexpr float QualityManager::kIncreaseLayerBitrateThreshold;

QualityManager::QualityManager(std::shared_ptr<Clock> the_clock)
  : initialized_{false}, enabled_{false}, padding_enabled_{false}, forced_layers_{false},
  slideshow_mode_active_{false}, spatial_layer_{0}, temporal_layer_{0}, max_active_spatial_layer_{0},
  max_active_temporal_layer_{0}, current_estimated_bitrate_{0}, last_quality_check_{the_clock->now()},
  last_activity_check_{the_clock->now()}, clock_{the_clock} {}

void QualityManager::enable() {
  ELOG_DEBUG("message: Enabling QualityManager");
  enabled_ = true;
  if (!forced_layers_) {
    setPadding(true);
  }
}

void QualityManager::disable() {
  ELOG_DEBUG("message: Disabling QualityManager");
  enabled_ = false;
  setPadding(false);
}

void QualityManager::notifyQualityUpdate() {
  if (!enabled_) {
    return;
  }

  if (!initialized_) {
    auto pipeline = getContext()->getPipelineShared();
    if (!pipeline) {
      return;
    }
    stats_ = pipeline->getService<Stats>();
    if (!stats_) {
      return;
    }
    if (!stats_->getNode()["total"].hasChild("senderBitrateEstimation")) {
      return;
    }
    initialized_ = true;
  }

  if (forced_layers_) {
    return;
  }
  time_point now = clock_->now();
  current_estimated_bitrate_ = stats_->getNode()["total"]["senderBitrateEstimation"].value();
  uint64_t current_layer_instant_bitrate = getInstantLayerBitrate(spatial_layer_, temporal_layer_);
  bool estimated_is_under_layer_bitrate = current_estimated_bitrate_ < current_layer_instant_bitrate;

  if (now - last_activity_check_ > kActiveLayerInterval) {
    calculateMaxActiveLayer();
    last_activity_check_ = now;
  }

  bool layer_is_active = spatial_layer_ <= max_active_spatial_layer_;

  if (!layer_is_active || (estimated_is_under_layer_bitrate && !slideshow_mode_active_)) {
    ELOG_DEBUG("message: Forcing calculate new layer, "
        "estimated_is_under_layer_bitrate: %d, layer_is_active: %d", estimated_is_under_layer_bitrate,
        layer_is_active);
    selectLayer(false);
  } else if (now - last_quality_check_ > kMinLayerSwitchInterval) {
    selectLayer(true);
  }
}

void QualityManager::selectLayer(bool try_higher_layers) {
  last_quality_check_ = clock_->now();
  int aux_temporal_layer = 0;
  int aux_spatial_layer = 0;
  int next_temporal_layer = 0;
  int next_spatial_layer = 0;
  float bitrate_margin = try_higher_layers ? kIncreaseLayerBitrateThreshold : 0;
  bool below_min_layer = true;
  ELOG_DEBUG("Calculate best layer with %lu, current layer %d/%d",
      current_estimated_bitrate_, spatial_layer_, temporal_layer_);
  for (auto &spatial_layer_node : stats_->getNode()["qualityLayers"].getMap()) {
    for (auto &temporal_layer_node : stats_->getNode()["qualityLayers"][spatial_layer_node.first.c_str()].getMap()) {
      ELOG_DEBUG("Bitrate for layer %d/%d %lu",
          aux_spatial_layer, aux_temporal_layer, temporal_layer_node.second->value());
      if (temporal_layer_node.second->value() != 0 &&
          (1. + bitrate_margin) * temporal_layer_node.second->value() < current_estimated_bitrate_) {
        next_temporal_layer = aux_temporal_layer;
        next_spatial_layer = aux_spatial_layer;
        below_min_layer = false;
      }
      aux_temporal_layer++;
    }
    aux_temporal_layer = 0;
    aux_spatial_layer++;
  }

  if (below_min_layer != slideshow_mode_active_) {
    if (below_min_layer || try_higher_layers) {
      slideshow_mode_active_ = below_min_layer;
      ELOG_DEBUG("Slideshow fallback mode %d", slideshow_mode_active_);
      WebRtcConnection *connection = getContext()->getPipelineShared()->getService<WebRtcConnection>().get();
      if (connection) {
        connection->notifyUpdateToHandlers();
      }
    }
  }

  if (next_temporal_layer != temporal_layer_ || next_spatial_layer != spatial_layer_) {
    ELOG_DEBUG("message: Layer Switch, current_layer: %d/%d, new_layer: %d/%d",
        spatial_layer_, temporal_layer_, next_spatial_layer, next_temporal_layer);
    setTemporalLayer(next_temporal_layer);
    setSpatialLayer(next_spatial_layer);

    // TODO(javier): should we wait for the actual spatial switch?
    // should we disable padding temporarily to avoid congestion (old padding + new bitrate)?
    setPadding(!isInMaxLayer());
    ELOG_DEBUG("message: Is padding enabled, padding_enabled_: %d", padding_enabled_);
  }
}

void QualityManager::calculateMaxActiveLayer() {
  int max_active_spatial_layer = 5;
  int max_active_temporal_layer = 5;

  for (; max_active_spatial_layer > 0; max_active_spatial_layer--) {
    if (getInstantLayerBitrate(max_active_spatial_layer, 0) > 0) {
      break;
    }
  }
  for (; max_active_temporal_layer > 0; max_active_temporal_layer--) {
    if (getInstantLayerBitrate(max_active_spatial_layer, max_active_temporal_layer) > 0) {
      break;
    }
  }
  stats_->getNode()["qualityLayers"].insertStat("maxActiveSpatialLayer",
      CumulativeStat{static_cast<uint64_t>(max_active_spatial_layer_)});
  stats_->getNode()["qualityLayers"].insertStat("maxActiveTemporalLayer",
      CumulativeStat{static_cast<uint64_t>(max_active_temporal_layer_)});

  max_active_spatial_layer_ = max_active_spatial_layer;
  max_active_temporal_layer_ = max_active_temporal_layer;
}

uint64_t QualityManager::getInstantLayerBitrate(int spatial_layer, int temporal_layer) {
  if (!stats_->getNode()["qualityLayers"].hasChild(spatial_layer) ||
      !stats_->getNode()["qualityLayers"][spatial_layer].hasChild(temporal_layer)) {
    return 0;
  }

  MovingIntervalRateStat* layer_stat =
    reinterpret_cast<MovingIntervalRateStat*>(&stats_->getNode()["qualityLayers"][spatial_layer][temporal_layer]);
  return layer_stat->value(kActiveLayerInterval);
}

bool QualityManager::isInBaseLayer() {
  return (spatial_layer_ == 0 && temporal_layer_ == 0);
}

bool QualityManager::isInMaxLayer() {
  return (spatial_layer_ >= max_active_spatial_layer_ && temporal_layer_ >= max_active_temporal_layer_);
}

void QualityManager::forceLayers(int spatial_layer, int temporal_layer) {
  if (spatial_layer == -1 || temporal_layer == -1) {
    forced_layers_ = false;
    return;
  }
  forced_layers_ = true;
  setPadding(false);

  spatial_layer_ = spatial_layer;
  temporal_layer_ = temporal_layer;
}

void QualityManager::setSpatialLayer(int spatial_layer) {
  if (!forced_layers_) {
    spatial_layer_ = spatial_layer;
  }
  stats_->getNode()["qualityLayers"].insertStat("selectedSpatialLayer",
      CumulativeStat{static_cast<uint64_t>(spatial_layer_)});
}
void QualityManager::setTemporalLayer(int temporal_layer) {
  if (!forced_layers_) {
    temporal_layer_ = temporal_layer;
  }
  stats_->getNode()["qualityLayers"].insertStat("selectedTemporalLayer",
      CumulativeStat{static_cast<uint64_t>(temporal_layer_)});
}

void QualityManager::setPadding(bool enabled) {
  if (padding_enabled_ != enabled) {
    padding_enabled_ = enabled;
    getContext()->getPipelineShared()->getService<WebRtcConnection>()->notifyUpdateToHandlers();
  }
}

}  // namespace erizo
<|endoftext|>