Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

class SISLConan(ConanFile):
name = "sisl"
version = "8.5.4"
version = "8.6.0"
homepage = "https://github.com/eBay/sisl"
description = "Library for fast data structures, utilities"
topics = ("ebay", "components", "core", "efficiency")
Expand Down
43 changes: 16 additions & 27 deletions include/sisl/sobject/sobject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,6 @@

namespace sisl {

// Each object is uniquely identified by its type and name.
// Ex: type=volume and name=volume_1, type=module and name=HomeBlks.
struct sobject_id {
std::string type;
std::string name;
bool empty() const { return type.empty() && name.empty(); }
[[maybe_unused]] bool operator<(const sobject_id& id) const {
return type < id.type || ((type == id.type) && (name < id.name));
}
};

typedef struct status_request {
nlohmann::json json;
bool do_recurse{false};
Expand All @@ -59,11 +48,6 @@ class sobject;
class sobject_manager;
using sobject_ptr = std::shared_ptr< sobject >;

// To search using only the type as key.
[[maybe_unused]] static bool operator<(const sobject_id& id, const std::string& key_type) { return id.type < key_type; }

[[maybe_unused]] static bool operator<(const std::string& key_type, const sobject_id& id) { return key_type < id.type; }

[[maybe_unused]] static status_response status_error(std::string error_str) {
status_response response;
response.json["error"] = error_str;
Expand All @@ -73,11 +57,11 @@ using sobject_ptr = std::shared_ptr< sobject >;
// Similar to sysfs kobject, sobject is a lightweight utility to create relationships
// between different classes and modules. This can be used to get or change the state of a class
// and all its children. Modules/subsystems which register their callbacks to be
// whenever a get status is called from the root or directly.
// whenever a get status is called from the root or directly. Object is uniquely identified by its name.
class sobject {
public:
sobject(sobject_manager* mgr, const std::string& obj_type, const std::string& obj_name, status_callback_type cb) :
m_mgr(mgr), m_id{obj_type, obj_name}, m_status_cb(std::move(cb)) {}
m_mgr(mgr), m_type(obj_type), m_name(obj_name), m_status_cb(std::move(cb)) {}

static sobject_ptr create(sobject_manager* mgr, const std::string& obj_type, const std::string& obj_name,
status_callback_type cb) {
Expand All @@ -88,34 +72,39 @@ class sobject {
status_response run_callback(const status_request& request) const;
sobject_ptr get_child(const std::string& name);
void add_child(const sobject_ptr child);
void add_child_type(const std::string& child_type);

sobject_id id() const { return m_id; }
std::string name() const { return m_id.name; }
std::string type() const { return m_id.type; }
std::string name() const { return m_name; }
std::string type() const { return m_type; }

private:
sobject_manager* m_mgr;
sobject_id m_id;
std::string m_type;
std::string m_name;
std::shared_mutex m_mtx;
status_callback_type m_status_cb;
// Keep a graph of child nodes. Mapping from name to child status object.
std::map< sobject_id, sobject_ptr > m_children;
std::map< std::string, sobject_ptr > m_children;
friend class sobject_manager;
};

class sobject_manager {
private:
public:
sobject_ptr create_object(const std::string& type, const std::string& name, status_callback_type cb);
status_response get_status(const status_request& request);

status_response get_child_type_status( const status_request& request);
status_response get_object_by_path(const status_request& request);
status_response get_object_status(const sobject_id& id, const status_request& request);
status_response get_object_status(const std::string& name, const status_request& request);
status_response get_objects(const status_request& request);
status_response get_object_types();
status_response get_object_types(const std::string& type);
void add_object_type(const std::string& parent_type, const std::string& child_type);

private:
// Mapping from object name to object metadata.
std::map< sobject_id, sobject_ptr, std::less<> > m_object_store;
// Mapping from object name to object metadata. Object names are required
// to be unique.
std::map< std::string, sobject_ptr, std::less<> > m_object_store;
// Mapping from parent type to set of all children type to display the schema.
std::map< std::string, std::set< std::string > > m_object_types;
std::shared_mutex m_mtx;
Expand Down
141 changes: 81 additions & 60 deletions src/sobject/sobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,46 +22,48 @@ namespace sisl {

sobject_ptr sobject::get_child(const std::string& name) {
std::shared_lock lock{m_mtx};
for (const auto& [id, obj] : m_children) {
// Return the first child found. We assume if user asks for a path
// there is a unique child in the parent.
if (id.name == name) { return obj; }
}
return nullptr;
auto iter = m_children.find(name);
Comment thread
yamingk marked this conversation as resolved.
if (iter == m_children.end()) { return nullptr; }
return iter->second;
}

void sobject::add_child(const sobject_ptr child) {
// Add a child to current object.
std::unique_lock lock{m_mtx};
LOGINFO("Parent {}/{} added child {}/{}", type(), name(), child->type(), child->name());
m_children.emplace(child->id(), child);
m_children.emplace(child->name(), child);
m_mgr->add_object_type(type(), child->type());
}

void sobject::add_child_type(const std::string& child_type) {
std::unique_lock lock{m_mtx};
LOGINFO("Added type parent {} child {}", type(), child_type);
m_mgr->add_object_type(type(), child_type);
}

status_response sobject::run_callback(const status_request& request) const {
status_response response;
response.json = nlohmann::json::object();
response.json["type"] = m_id.type;
response.json["name"] = m_id.name;
response.json["type"] = m_type;
response.json["name"] = m_name;
auto res = m_status_cb(request).json;
if (!res.is_null()) { response.json.update(res); }
response.json["children"] = nlohmann::json::object();

for (const auto& [id, obj] : m_children) {
if (response.json["children"][id.type] == nullptr) {
if (request.do_recurse) {
response.json["children"][id.type] == nlohmann::json::object();
} else {
response.json["children"][id.type] == nlohmann::json::array();
}

for (const auto& [name, obj] : m_children) {
auto child_type = obj->type();
auto child_name = obj->name();
if (response.json["children"] == nullptr) { response.json["children"] = nlohmann::json::object(); }

if (response.json["children"][child_type] == nullptr) {
response.json["children"][child_type] == nlohmann::json::array();
}

if (request.do_recurse) {
// Call recursive.
auto child_json = obj->run_callback(request).json;
response.json["children"][id.type].emplace(id.name, child_json);
response.json["children"][child_type].emplace_back(child_json);
} else {
response.json["children"][id.type].push_back(id.name);
response.json["children"][child_type].emplace_back(child_name);
}
}

Expand All @@ -71,8 +73,7 @@ status_response sobject::run_callback(const status_request& request) const {
sobject_ptr sobject_manager::create_object(const std::string& type, const std::string& name, status_callback_type cb) {
std::unique_lock lock{m_mtx};
auto obj = sobject::create(this, type, name, std::move(cb));
sobject_id id{type, name};
m_object_store[id] = obj;
m_object_store[name] = obj;
if (m_object_types.count(type) == 0) { m_object_types[type] = {}; }
LOGINFO("Created status object type={} name={}", type, name);
return obj;
Expand All @@ -83,68 +84,78 @@ void sobject_manager::add_object_type(const std::string& parent_type, const std:
m_object_types[parent_type].insert(child_type);
}

status_response sobject_manager::get_object_types() {
status_response sobject_manager::get_object_types(const std::string& type) {
status_response response;

for (const auto& [type, children] : m_object_types) {
response.json[type] = nlohmann::json::array();
for (const auto& child_type : children) {
response.json[type].emplace_back(child_type);
}
auto children = nlohmann::json::object();
for (const auto& child : m_object_types[type]) {
children.emplace(child, get_object_types(child).json);
}
response.json = children;
return response;
}

status_response sobject_manager::get_objects(const status_request& request) {
status_response response;

// We by default start from the 'module' types recursively as they are
// the top of the heirarchy.
std::string obj_type = request.obj_type.empty() ? "module" : request.obj_type;
auto iter = m_object_store.begin();
if (!request.next_cursor.empty()) {
// Extract cursor which is of format "type:name"
auto index = request.next_cursor.find_first_of("^");
if (index == std::string::npos) return status_error("Invalid cursor");
auto type = request.next_cursor.substr(0, index);
auto name = request.next_cursor.substr(index + 1);
iter = m_object_store.find(sobject_id{type, name});
// Extract cursor which has name.
iter = m_object_store.find(request.next_cursor);
if (iter == m_object_store.end()) return status_error("Cursor not found");
} else if (request.obj_name.empty() && !request.obj_type.empty()) {
// Get all objects of type requested.
iter = m_object_store.find(request.obj_type);
}

int batch_size = request.batch_size;
while (iter != m_object_store.end() && batch_size > 0) {
if (request.obj_name.empty() && !request.obj_type.empty() && request.obj_type != iter->first.type) {
// If only one type of objects requested.
return response;
if (obj_type != iter->second->type()) {
iter++;
continue;
}

response.json[iter->first.name] = iter->second->run_callback(request).json;
response.json[iter->first] = iter->second->run_callback(request).json;
iter++;
batch_size--;
}

if (iter != m_object_store.end()) { response.json["next_cursor"] = iter->first.type + "^" + iter->first.name; }
if (iter != m_object_store.end() && obj_type == iter->second->type()) {
response.json["next_cursor"] = iter->second->name();
}

return response;
}

status_response sobject_manager::get_object_status(const sobject_id& id, const status_request& request) {
auto iter = m_object_store.find(id);
status_response sobject_manager::get_object_status(const std::string& name, const status_request& request) {
auto iter = m_object_store.find(name);
if (iter == m_object_store.end()) { return status_error("Object identifier not found"); }
return iter->second->run_callback(request);
}

status_response sobject_manager::get_object_by_path(const status_request& request) {
sobject_ptr obj = nullptr;
for (const auto& [id, obj_ptr] : m_object_store) {
if (id.name == request.obj_path[0]) {
obj = obj_ptr;
break;
status_response sobject_manager::get_child_type_status( const status_request& request) {
status_response response;
auto iter = m_object_store.find(request.obj_name);
if (iter == m_object_store.end()) { return status_error("Object identifier not found"); }
for (const auto& [child_name, child_obj] : iter->second->m_children) {
if (child_obj->type() == request.obj_type) {
response.json[child_name] = child_obj->run_callback(request).json;
}
}
if (!response.json.empty()) {
// If we found child in object tree return it.
return response;
}

// Else ask the parent object to do the work. This is used to lazily
// get objects of type which are not created by default.
return iter->second->run_callback(request);
}

if (obj == nullptr) { return status_error("Object identifier not found"); }
status_response sobject_manager::get_object_by_path(const status_request& request) {
sobject_ptr obj = nullptr;
auto iter = m_object_store.find(request.obj_path[0]);
if (iter == m_object_store.end()) { return status_error("Object identifier not found"); }
obj = iter->second;
for (uint32_t ii = 1; ii < request.obj_path.size(); ii++) {
obj = obj->get_child(request.obj_path[ii]);
if (obj == nullptr) { return status_error("Object identifier not found"); }
Expand All @@ -160,19 +171,29 @@ status_response sobject_manager::get_status(const status_request& request) {
return get_object_by_path(request);
}

// If both are empty, we return all the types. If both not empty, we return the specific object.
// Its an error to have name non empty and type empty.
if (!request.obj_name.empty() && request.obj_type.empty()) { return status_error("Type details not given"); }
if (!request.obj_type.empty() && !request.obj_name.empty()) {
// Get all children under the parent of type.
return get_child_type_status(request);
}

if (!request.obj_name.empty() && !request.obj_type.empty()) {
if (!request.obj_name.empty()) {
// Return specific object.
sobject_id id{request.obj_type, request.obj_name};
return get_object_status(std::move(id), request);
return get_object_status(request.obj_name, request);
}

if (request.obj_name.empty() && request.obj_type.empty()) { return get_object_types(); }
if (!request.obj_type.empty()) {
// Return all objects of this type.
return get_objects(request);
}

if (!request.do_recurse) {
// If no recurse we only return the types.
status_response response;
response.json["module"] = get_object_types("module").json;
return response;
}

// Dump all objects.
// Dump all objects recursively.
return get_objects(request);
}

Expand Down
Loading