diff --git a/src/api/algo.cpp b/src/api/algo.cpp index b310183a..aa0bb9ab 100644 --- a/src/api/algo.cpp +++ b/src/api/algo.cpp @@ -48,11 +48,10 @@ auto Algo::hash(const pjs::Value &value) -> size_t { // Cache::Options::Options(pjs::Object *options) { - thread_local static pjs::ConstStr str_size("size"), str_ttl("ttl"); - Value(options, str_size) + Value(options, "size") .get(size) .check_nullable(); - Value(options, str_ttl) + Value(options, "ttl") .get_seconds(ttl) .check_nullable(); } @@ -507,6 +506,100 @@ void Quota::Counter::finalize() { }); } +// +// SharedMap +// + +SharedMap::SharedMap(pjs::Str *name) + : m_map(Map::get(name->str())) +{ +} + +auto SharedMap::size() -> size_t { + return m_map->size(); +} + +void SharedMap::clear() { + m_map->clear(); +} + +bool SharedMap::erase(pjs::Str *key) { + return m_map->erase(key->data()); +} + +bool SharedMap::has(pjs::Str *key) { + return m_map->has(key->data()); +} + +bool SharedMap::get(pjs::Str *key, pjs::Value &value) { + pjs::SharedValue sv; + if (m_map->get(key->data(), sv)) { + sv.to_value(value); + return true; + } else { + return false; + } +} + +void SharedMap::set(pjs::Str *key, const pjs::Value &value) { + pjs::SharedValue sv(value); + m_map->set(key->data(), sv); +} + +// +// SharedMap::Map +// + +std::map SharedMap::Map::m_maps; +std::mutex SharedMap::Map::m_maps_mutex; + +auto SharedMap::Map::get(const std::string &name) -> Map* { + std::lock_guard lock(m_maps_mutex); + auto &p = m_maps[name]; + if (!p) { + p = new Map; + p->retain(); + } + return p; +} + +auto SharedMap::Map::size() -> size_t { + std::lock_guard lock(m_mutex); + return m_map.size(); +} + +void SharedMap::Map::clear() { + std::lock_guard lock(m_mutex); + m_map.clear(); +} + +bool SharedMap::Map::erase(pjs::Str::CharData *key) { + std::lock_guard lock(m_mutex); + auto i = m_map.find(key); + if (i == m_map.end()) return false; + m_map.erase(i); + return true; +} + +bool SharedMap::Map::has(pjs::Str::CharData *key) { + std::lock_guard lock(m_mutex); + auto i = m_map.find(key); + return i != m_map.end(); +} + +bool SharedMap::Map::get(pjs::Str::CharData *key, pjs::SharedValue &value) { + std::lock_guard lock(m_mutex); + auto i = m_map.find(key); + if (i == m_map.end()) return false; + value = i->second; + return true; +} + +void SharedMap::Map::set(pjs::Str::CharData *key, const pjs::SharedValue &value) { + std::lock_guard lock(m_mutex); + m_map[key] = value; +} + // // URLRouter // @@ -1469,8 +1562,16 @@ template<> void ClassDef::init() { ctor([](Context &ctx) -> Object* { Function *allocate = nullptr, *free = nullptr; Object *options = nullptr; - if (!ctx.arguments(0, &allocate, &free, &options)) return nullptr; - return Cache::make(options, allocate, free); + if ( + ctx.try_arguments(2, &allocate, &free, &options) || + ctx.try_arguments(1, &allocate, &options) || + ctx.try_arguments(0, &options) + ) { + return Cache::make(options, allocate, free); + } else { + ctx.error_argument_type(0, "a function or an object"); + return nullptr; + } }); method("get", [](Context &ctx, Object *obj, Value &ret) { @@ -1555,6 +1656,54 @@ template<> void ClassDef>::init() { ctor(); } +// +// SharedMap +// + +template<> void ClassDef::init() { + ctor([](Context &ctx) -> Object * { + Str *name; + if (!ctx.arguments(1, &name)) return nullptr; + return SharedMap::make(name); + }); + + accessor("size", [](Object *obj, Value &ret) { ret.set((int)obj->as()->size()); }); + + method("clear", [](Context &ctx, Object *obj, Value &ret) { + obj->as()->clear(); + }); + + method("delete", [](Context &ctx, Object *obj, Value &ret) { + Str *key; + if (!ctx.arguments(1, &key)) return; + ret.set(obj->as()->erase(key)); + }); + + method("has", [](Context &ctx, Object *obj, Value &ret) { + Str *key; + if (!ctx.arguments(1, &key)) return; + ret.set(obj->as()->has(key)); + }); + + method("get", [](Context &ctx, Object *obj, Value &ret) { + Str *key; + if (!ctx.arguments(1, &key)) return; + if (!obj->as()->get(key, ret)) ret = Value::undefined; + }); + + method("set", [](Context &ctx, Object *obj, Value &ret) { + Str *key; + Value value; + if (!ctx.arguments(2, &key, &value)) return; + obj->as()->set(key, value); + }); +} + +template<> void ClassDef>::init() { + super(); + ctor(); +} + // // URLRouter // @@ -1882,6 +2031,7 @@ template<> void ClassDef::init() { ctor(); variable("Cache", class_of>()); variable("Quota", class_of>()); + variable("SharedMap", class_of>()); variable("URLRouter", class_of>()); variable("LoadBalancer", class_of>()); variable("HashingLoadBalancer", class_of>()); diff --git a/src/api/algo.hpp b/src/api/algo.hpp index a54ecfb5..78c2fa34 100644 --- a/src/api/algo.hpp +++ b/src/api/algo.hpp @@ -217,6 +217,64 @@ class Quota : public pjs::ObjectTemplate { friend class pjs::ObjectTemplate; }; +// +// SharedMap +// + +class SharedMap : public pjs::ObjectTemplate { +public: + SharedMap(pjs::Str *name); + + auto size() -> size_t; + void clear(); + bool erase(pjs::Str *key); + bool has(pjs::Str *key); + bool get(pjs::Str *key, pjs::Value &value); + void set(pjs::Str *key, const pjs::Value &value); + +private: + + // + // SharedMap::Map + // + + class Map : public pjs::RefCountMT { + public: + static auto get(const std::string &name) -> Map*; + + auto size() -> size_t; + void clear(); + bool erase(pjs::Str::CharData *key); + bool has(pjs::Str::CharData *key); + bool get(pjs::Str::CharData *key, pjs::SharedValue &value); + void set(pjs::Str::CharData *key, const pjs::SharedValue &value); + + private: + typedef pjs::Ref Key; + + struct Hash { + size_t operator()(const Key &k) const { + std::hash h; + return h(k->str()); + } + }; + + struct EqualTo { + bool operator()(const Key &a, const Key &b) const { + return a->str() == b->str(); + } + }; + + std::unordered_map m_map; + std::mutex m_mutex; + + static std::map m_maps; + static std::mutex m_maps_mutex; + }; + + pjs::Ref m_map; +}; + // // ResourcePool // diff --git a/src/pjs/types.cpp b/src/pjs/types.cpp index d03139b1..7b216556 100644 --- a/src/pjs/types.cpp +++ b/src/pjs/types.cpp @@ -878,7 +878,17 @@ void SharedValue::to_value(Value &v) const { } } -void SharedValue::from_value(const Value &v) { +void SharedValue::assign(const SharedValue &v) { + switch (m_t = v.m_t) { + case Value::Type::Boolean: m_v.b = v.m_v.b; break; + case Value::Type::Number: m_v.n = v.m_v.n; break; + case Value::Type::String: m_v.s = v.m_v.s->retain(); break; + case Value::Type::Object: if (auto o = m_v.o = v.m_v.o) o->retain(); break; + default: break; + } +} + +void SharedValue::assign(const Value &v) { switch (m_t = v.type()) { case Value::Type::Boolean: m_v.b = v.b(); break; case Value::Type::Number: m_v.n = v.n(); break; @@ -930,8 +940,9 @@ auto SharedObject::to_object() -> Object* { for (auto i = 0, n = b->length; i < n; i++) { const auto &e = b->entries[i]; if (e.k) { + Ref k(Str::make(e.k)); Value v; e.v.to_value(v); - obj->set(Str::make(e.k), v); + obj->set(k, v); } } } diff --git a/src/pjs/types.hpp b/src/pjs/types.hpp index 25896d99..83a2bc14 100644 --- a/src/pjs/types.hpp +++ b/src/pjs/types.hpp @@ -1913,10 +1913,12 @@ inline auto Value::value_of() const -> double { class SharedValue { public: SharedValue() : m_t(Value::Type::Empty) {} - SharedValue(const Value &v) { from_value(v); } + SharedValue(const SharedValue &v) { assign(v); } + SharedValue(const Value &v) { assign(v); } ~SharedValue() { release(); } - auto operator=(const Value &v) -> SharedValue& { release(); from_value(v); return *this; } + auto operator=(const SharedValue &v) -> SharedValue& { release(); assign(v); return *this; } + auto operator=(const Value &v) -> SharedValue& { release(); assign(v); return *this; } void to_value(Value &v) const; private: @@ -1928,7 +1930,8 @@ class SharedValue { SharedObject* o; } m_v; - void from_value(const Value &v); + void assign(const SharedValue &v); + void assign(const Value &v); void release(); }; diff --git a/src/thread.hpp b/src/thread.hpp index 00c8937b..63131686 100644 --- a/src/thread.hpp +++ b/src/thread.hpp @@ -30,8 +30,6 @@ namespace pipy { -class WorkerThread; - // // Thread //