blob: 081339686cc9396079acf21a3ad2617aa915234f [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PORTIER_GROUP_H_
#define PORTIER_GROUP_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/logging.h>
#include <base/macros.h>
namespace portier {
// Groups names should be easy to type/remember group names. These
// names will likely be typed on a shell. Group names can contain
// alphanumeric characters or underscores.
bool IsValidGroupName(const std::string& group_name);
// The Group class is used to track all the group members of a
// group. When adding members to the group, the group will add
// a reference to itself to the added member. This allows for both the
// member and the group to agree on membership.
//
// The relationship between group members and groups is many-to-one.
//
// +---------+ +-----------+
// | |-----(Strong Reference)---->| |
// | | * | Group |
// | Group | | Member |
// | |<-----(Weak Reference)------|(interface)|
// +---------+ +-----------+
// A
// |
// +------------+
// | Concrete |
// | Member |
// +------------+
//
// The template parameter |MemberType| must be a type which implements
// GroupMemberInterface.
template <class MemberType>
class Group {
public:
using iterator = typename std::vector<std::shared_ptr<MemberType>>::iterator;
// Creates a new group, returning a shared pointer to the group.
// Returns and empty pointer if the group name is invalid.
static std::unique_ptr<Group> Create(const std::string& name);
~Group() { RemoveAllMembers(); }
// Returns the name of the group.
std::string name() const { return name_; }
// Number of members in group.
size_t size() const { return members_.size(); }
// Returns a copy of the list of all group members.
std::vector<std::shared_ptr<MemberType>> GetMembers() const {
return members_;
}
// An iterator for going through all the members.
iterator begin() { return members_.begin(); }
iterator end() { return members_.end(); }
// Adds a new member to the group.
// If the provided |member| is not a member of any group, then:
// 1 - The member will be added to the this group,
// 2 - The member will be given a reference to this group,
// 3 - The `PostJoinGroup()` hock will be called on the member,
// 4 - Function will return true.
//
// If the provided |member| is already a member of this group, then
// no action is performed and true is returned.
//
// If the provided |member| is already part of a different group,
// then false is returned.
bool AddMember(std::shared_ptr<MemberType> member);
// Removes a specified member from the group.
// If |member| pointers to a member of the group, then:
// 1 - The member will be remove from the list of group members,
// 2 - The member's copy of the groups reference will be reset,
// 3 - The `PostLeaveGroup()` hook will be called on the member.
//
// If the provided |member| if not a member of the group, then
// no change is made and false is returned.
bool RemoveMember(std::shared_ptr<MemberType> member);
// Removes all of the current members from the group. This will
// call the `PostLeaveGroup()` hook on all members being removed.
void RemoveAllMembers();
// Gets the current upstream interface if set, otherwise returns a
// nullptr.
std::shared_ptr<MemberType> GetUpstream() const { return upstream_; }
// Sets the group's upstream interface. Specified |member| must be a
// member of the group or call will fail and return false.
// This will replace the current upstream if already set. Returns
// true even if the current upstream is the same interface.
bool SetUpstream(std::shared_ptr<MemberType> member);
// Removes the upstream interface as the upstream interface. The
// interface will remain a member of the group.
void UnsetUpstream() { upstream_.reset(); }
private:
// Constructor to the interface is private to enforce the
// creation of a shared pointer to the interface.
explicit Group(const std::string& name) : name_(name) {}
// Removes a member from the group via an interation position to the
// the internal |members_| vector. This will remove the group from
// the member and call the `PostLeaveGroup()` hook. Returns a copy
// of the same iterator returned by the call to
// |members_.erase(position)|.
// This function is intended to be used by the public methods
// `RemoveMember()` and `RemoveAllMembers()`.
iterator Remove(iterator position);
// Name of the group.
std::string name_;
// A list of all members.
std::vector<std::shared_ptr<MemberType>> members_;
// A pointer to the upstream interface.
std::shared_ptr<MemberType> upstream_;
DISALLOW_COPY_AND_ASSIGN(Group);
}; // class Group
template <class MemberType>
std::unique_ptr<Group<MemberType>> Group<MemberType>::Create(
const std::string& name) {
using GroupType = Group<MemberType>;
if (!IsValidGroupName(name)) {
return nullptr;
}
auto group_ptr = std::unique_ptr<GroupType>(new GroupType(name));
return std::move(group_ptr);
}
template <class MemberType>
bool Group<MemberType>::AddMember(std::shared_ptr<MemberType> member) {
DCHECK(member);
if (member->HasGroup()) {
// Return true if the member is already a member of the group, and
// false if the group is different. Either way, no other action is
// required.
return member->GetGroup() == this;
}
members_.push_back(member);
member->SetGroup(this);
member->PostJoinGroup();
return true;
}
template <class MemberType>
bool Group<MemberType>::RemoveMember(std::shared_ptr<MemberType> member) {
DCHECK(member);
if (!member->HasGroup() || member->GetGroup() != this) {
return false;
}
auto it = std::find(members_.begin(), members_.end(), member);
// The member must be part of the list, otherwise, there is a bug
// that has somehow released the member from the group without
// removing the reference to the group.
DCHECK(it != members_.end());
Remove(it);
return true;
}
template <class MemberType>
void Group<MemberType>::RemoveAllMembers() {
auto it = members_.begin();
while (it != members_.end()) {
it = Remove(it);
}
}
template <class MemberType>
bool Group<MemberType>::SetUpstream(std::shared_ptr<MemberType> member) {
DCHECK(member);
if (!member->HasGroup() || member->GetGroup() != this) {
return false;
}
upstream_ = member;
return true;
}
// private.
template <class MemberType>
typename Group<MemberType>::iterator Group<MemberType>::Remove(iterator it) {
DCHECK(it != members_.end());
std::shared_ptr<MemberType> member = *it;
if (member == upstream_) {
upstream_.reset();
}
auto next_it = members_.erase(it);
member->ResetGroup();
member->PostLeaveGroup();
return next_it;
}
// Group Member interface.
template <class DerivedType>
class GroupMemberInterface {
public:
using GroupType = Group<DerivedType>;
GroupMemberInterface() : group_(nullptr) {}
virtual ~GroupMemberInterface() = default;
// Returns true if the member is a member of a group.
bool HasGroup() const { return group_ != nullptr; }
// Returns true if the member is the upstream of the group.
bool IsUpstream() const {
return HasGroup() ? GetGroup()->GetUpstream().get() == this : false;
}
// Returns a pointer to the group which the member is a part
// of. If the member is not part of any group then the pointer
// returned is null.
GroupType* GetGroup() const { return group_; }
protected:
// Hook methods for classes which inherit from this. Used to
// indicate to the subclass that they have been removed from a
// group.
// Call after a member has been added to a group.
virtual void PostJoinGroup() = 0;
// Called after the member has been removed from a group.
virtual void PostLeaveGroup() = 0;
private:
// Provides a weak reference to the group which the member is a part
// of.
void SetGroup(GroupType* group) { group_ = group; }
void ResetGroup() { group_ = nullptr; }
// A pointer to the group which the member is a part of. It is
// expected that the Group class will clear this pointer when
// removing the member, or when the group's destructor is called.
GroupType* group_;
// Used to all the Group class to access the `SetGroup()`
// and `ResetGroup()` methods as well as the post join/leave
// group hooks when adding and removing the member from the group.
friend class Group<DerivedType>;
DISALLOW_COPY_AND_ASSIGN(GroupMemberInterface);
}; // class GroupMember
} // namespace portier
#endif // PORTIER_GROUP_H_