Document exceptions and other details in storage v2 API
Summary: Most instances of `@throw std::bad_alloc` are left unexplained as these functions perform general heap allocations are it's obvious from the function name that it will do so. Basically anything with `Create`, `Make` or `Build` implies allocations. Additionally, which parts exactly perform allocations are an implementation detail. Functions which do unexpected heap allocations have the reason stated in the documentation, these functions typically have exactly one spot which could raise such an exception. Some functions are marked as `noexcept`, these are usually "special functions" such as constructors and operators. This could potentially improve performance because STL may use API overloads that work faster with `noexcept` stuff. Remaining non-throwing functions aren't marked as `noexcept` as that wasn't our practice nor is common in our codebase. On the other hand, if we continue enforcing the documentation of thrown exceptions, perhaps we should start using `noexcept`. Reviewers: mferencevic, ipaljak Reviewed By: mferencevic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2350
This commit is contained in:
parent
02722a1ed5
commit
9a94205a07
@ -44,6 +44,7 @@ class CommitLog final {
|
||||
}
|
||||
|
||||
/// Mark a transaction as finished.
|
||||
/// @throw std::bad_alloc
|
||||
void MarkFinished(uint64_t id) {
|
||||
std::lock_guard<utils::SpinLock> guard(lock_);
|
||||
|
||||
@ -98,6 +99,7 @@ class CommitLog final {
|
||||
oldest_active_ = next_start_;
|
||||
}
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
Block *FindOrCreateBlock(uint64_t id) {
|
||||
if (!head_) {
|
||||
head_ = allocator_.allocate(1);
|
||||
|
@ -39,8 +39,10 @@ class EdgeAccessor final {
|
||||
/// @throw std::bad_alloc
|
||||
Result<bool> SetProperty(PropertyId property, const PropertyValue &value);
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
Result<PropertyValue> GetProperty(PropertyId property, View view) const;
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
Result<std::map<PropertyId, PropertyValue>> Properties(View view) const;
|
||||
|
||||
Gid Gid() const { return edge_->gid; }
|
||||
|
@ -110,6 +110,7 @@ bool AnyVersionHasLabel(Vertex *vertex, LabelId label, uint64_t timestamp) {
|
||||
/// Helper function for label-property index garbage collection. Returns true if
|
||||
/// there's a reachable version of the vertex that has the given label and
|
||||
/// property value.
|
||||
/// @throw std::bad_alloc if unable to copy the PropertyValue
|
||||
bool AnyVersionHasLabelProperty(Vertex *vertex, LabelId label, PropertyId key,
|
||||
const PropertyValue &value,
|
||||
uint64_t timestamp) {
|
||||
@ -230,6 +231,7 @@ bool CurrentVersionHasLabel(Vertex *vertex, LabelId label,
|
||||
// Helper function for iterating through label-property index. Returns true if
|
||||
// this transaction can see the given vertex, and the visible version has the
|
||||
// given label and property.
|
||||
// @throw std::bad_alloc if unable to copy the PropertyValue
|
||||
bool CurrentVersionHasLabelProperty(Vertex *vertex, LabelId label,
|
||||
PropertyId key, const PropertyValue &value,
|
||||
Transaction *transaction, View view) {
|
||||
|
@ -42,6 +42,7 @@ class LabelIndex {
|
||||
public:
|
||||
explicit LabelIndex(Indices *indices) : indices_(indices) {}
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
void UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx);
|
||||
|
||||
void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp);
|
||||
@ -87,6 +88,7 @@ class LabelIndex {
|
||||
};
|
||||
|
||||
/// Returns an self with vertices visible from the given transaction.
|
||||
/// @throw std::bad_alloc
|
||||
Iterable Vertices(LabelId label, View view, Transaction *transaction) {
|
||||
return Iterable(GetOrCreateStorage(label)->access(), label, view,
|
||||
transaction, indices_);
|
||||
@ -100,6 +102,7 @@ class LabelIndex {
|
||||
utils::SkipList<LabelStorage> index_;
|
||||
Indices *indices_;
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
utils::SkipList<Entry> *GetOrCreateStorage(LabelId label);
|
||||
};
|
||||
|
||||
@ -120,11 +123,14 @@ class LabelPropertyIndex {
|
||||
public:
|
||||
explicit LabelPropertyIndex(Indices *indices) : indices_(indices) {}
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
void UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx);
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
void UpdateOnSetProperty(PropertyId property, const PropertyValue &value,
|
||||
Vertex *vertex, const Transaction &tx);
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
bool CreateIndex(LabelId label, PropertyId property,
|
||||
utils::SkipList<Vertex>::Accessor vertices);
|
||||
|
||||
@ -136,10 +142,12 @@ class LabelPropertyIndex {
|
||||
return index_.find({label, property}) != index_.end();
|
||||
}
|
||||
|
||||
/// @throw std::bad_alloc if unable to copy a PropertyValue
|
||||
void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp);
|
||||
|
||||
class Iterable {
|
||||
public:
|
||||
/// @throw std::bad_alloc if unable to copy a PropertyValue
|
||||
Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label,
|
||||
PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
@ -148,6 +156,7 @@ class LabelPropertyIndex {
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
/// @throw std::bad_alloc raised in AdvanceUntilValid
|
||||
Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator);
|
||||
|
||||
VertexAccessor operator*() const { return current_vertex_accessor_; }
|
||||
@ -159,9 +168,11 @@ class LabelPropertyIndex {
|
||||
return index_iterator_ != other.index_iterator_;
|
||||
}
|
||||
|
||||
/// @throw std::bad_alloc raised in AdvanceUntilValid
|
||||
Iterator &operator++();
|
||||
|
||||
private:
|
||||
/// @throw std::bad_alloc if unable to copy a PropertyValue
|
||||
void AdvanceUntilValid();
|
||||
|
||||
Iterable *self_;
|
||||
@ -184,6 +195,7 @@ class LabelPropertyIndex {
|
||||
Indices *indices_;
|
||||
};
|
||||
|
||||
/// @throw std::bad_alloc if unable to copy a PropertyValue
|
||||
Iterable Vertices(
|
||||
LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
@ -234,6 +246,7 @@ struct Indices {
|
||||
|
||||
/// This function should be called from garbage collection to clean-up the
|
||||
/// index.
|
||||
/// @throw std::bad_alloc raised in LabelPropertyIndex::RemoveObsoleteEntries
|
||||
void RemoveObsoleteEntries(Indices *indices,
|
||||
uint64_t oldest_active_start_timestamp);
|
||||
|
||||
@ -242,10 +255,12 @@ void RemoveObsoleteEntries(Indices *indices,
|
||||
// view for use in Merge.
|
||||
|
||||
/// This function should be called whenever a label is added to a vertex.
|
||||
/// @throw std::bad_alloc
|
||||
void UpdateOnAddLabel(Indices *indices, LabelId label, Vertex *vertex,
|
||||
const Transaction &tx);
|
||||
|
||||
/// This function should be called whenever a property is modified on a vertex.
|
||||
/// @throw std::bad_alloc
|
||||
void UpdateOnSetProperty(Indices *indices, PropertyId property,
|
||||
const PropertyValue &value, Vertex *vertex,
|
||||
const Transaction &tx);
|
||||
|
@ -69,6 +69,7 @@ inline bool PrepareForWrite(Transaction *transaction, TObj *object) {
|
||||
/// a pointer to the created delta. It doesn't perform any linking of the delta
|
||||
/// and is primarily used to create the first delta for an object (that must be
|
||||
/// a `DELETE_OBJECT` delta).
|
||||
/// @throw std::bad_alloc
|
||||
inline Delta *CreateDeleteObjectDelta(Transaction *transaction) {
|
||||
transaction->EnsureCommitTimestampExists();
|
||||
return &transaction->deltas.emplace_back(Delta::DeleteObjectTag(),
|
||||
@ -78,6 +79,7 @@ inline Delta *CreateDeleteObjectDelta(Transaction *transaction) {
|
||||
|
||||
/// This function creates a delta in the transaction for the object and links
|
||||
/// the delta into the object's delta list.
|
||||
/// @throw std::bad_alloc
|
||||
template <typename TObj, class... Args>
|
||||
inline void CreateAndLinkDelta(Transaction *transaction, TObj *object,
|
||||
Args &&... args) {
|
||||
|
@ -32,6 +32,7 @@ class NameIdMapper final {
|
||||
};
|
||||
|
||||
public:
|
||||
/// @throw std::bad_alloc if unable to insert a new mapping
|
||||
uint64_t NameToId(const std::string &name) {
|
||||
auto name_to_id_acc = name_to_id_.access();
|
||||
auto found = name_to_id_acc.find(name);
|
||||
|
@ -39,16 +39,22 @@ class PropertyValue {
|
||||
}
|
||||
|
||||
// copy constructors for non-primitive types
|
||||
/// @throw std::bad_alloc
|
||||
explicit PropertyValue(const std::string &value) : type_(Type::String) {
|
||||
new (&string_v) std::string(value);
|
||||
}
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error if length of value exceeds
|
||||
/// std::string::max_length().
|
||||
explicit PropertyValue(const char *value) : type_(Type::String) {
|
||||
new (&string_v) std::string(value);
|
||||
}
|
||||
/// @throw std::bad_alloc
|
||||
explicit PropertyValue(const std::vector<PropertyValue> &value)
|
||||
: type_(Type::List) {
|
||||
new (&list_v) std::vector<PropertyValue>(value);
|
||||
}
|
||||
/// @throw std::bad_alloc
|
||||
explicit PropertyValue(const std::map<std::string, PropertyValue> &value)
|
||||
: type_(Type::Map) {
|
||||
new (&map_v) std::map<std::string, PropertyValue>(value);
|
||||
@ -68,12 +74,14 @@ class PropertyValue {
|
||||
}
|
||||
|
||||
// copy constructor
|
||||
/// @throw std::bad_alloc
|
||||
PropertyValue(const PropertyValue &other);
|
||||
|
||||
// move constructor
|
||||
PropertyValue(PropertyValue &&other) noexcept;
|
||||
|
||||
// copy assignment
|
||||
/// @throw std::bad_alloc
|
||||
PropertyValue &operator=(const PropertyValue &other);
|
||||
|
||||
// move assignment
|
||||
@ -95,18 +103,21 @@ class PropertyValue {
|
||||
bool IsMap() const { return type_ == Type::Map; }
|
||||
|
||||
// value getters for primitive types
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
bool ValueBool() const {
|
||||
if (type_ != Type::Bool) {
|
||||
throw PropertyValueException("The value isn't a bool!");
|
||||
}
|
||||
return bool_v;
|
||||
}
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
int64_t ValueInt() const {
|
||||
if (type_ != Type::Int) {
|
||||
throw PropertyValueException("The value isn't an int!");
|
||||
}
|
||||
return int_v;
|
||||
}
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
double ValueDouble() const {
|
||||
if (type_ != Type::Double) {
|
||||
throw PropertyValueException("The value isn't a double!");
|
||||
@ -115,6 +126,7 @@ class PropertyValue {
|
||||
}
|
||||
|
||||
// const value getters for non-primitive types
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
const std::string &ValueString() const {
|
||||
if (type_ != Type::String) {
|
||||
throw PropertyValueException("The value isn't a string!");
|
||||
@ -122,6 +134,7 @@ class PropertyValue {
|
||||
return string_v;
|
||||
}
|
||||
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
const std::vector<PropertyValue> &ValueList() const {
|
||||
if (type_ != Type::List) {
|
||||
throw PropertyValueException("The value isn't a list!");
|
||||
@ -129,6 +142,7 @@ class PropertyValue {
|
||||
return list_v;
|
||||
}
|
||||
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
const std::map<std::string, PropertyValue> &ValueMap() const {
|
||||
if (type_ != Type::Map) {
|
||||
throw PropertyValueException("The value isn't a map!");
|
||||
@ -137,6 +151,7 @@ class PropertyValue {
|
||||
}
|
||||
|
||||
// reference value getters for non-primitive types
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
std::string &ValueString() {
|
||||
if (type_ != Type::String) {
|
||||
throw PropertyValueException("The value isn't a string!");
|
||||
@ -144,6 +159,7 @@ class PropertyValue {
|
||||
return string_v;
|
||||
}
|
||||
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
std::vector<PropertyValue> &ValueList() {
|
||||
if (type_ != Type::List) {
|
||||
throw PropertyValueException("The value isn't a list!");
|
||||
@ -151,6 +167,7 @@ class PropertyValue {
|
||||
return list_v;
|
||||
}
|
||||
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
std::map<std::string, PropertyValue> &ValueMap() {
|
||||
if (type_ != Type::Map) {
|
||||
throw PropertyValueException("The value isn't a map!");
|
||||
@ -174,6 +191,7 @@ class PropertyValue {
|
||||
};
|
||||
|
||||
// stream output
|
||||
/// @throw anything std::ostream::operator<< may throw.
|
||||
inline std::ostream &operator<<(std::ostream &os,
|
||||
const PropertyValue::Type type) {
|
||||
switch (type) {
|
||||
@ -193,6 +211,7 @@ inline std::ostream &operator<<(std::ostream &os,
|
||||
return os << "map";
|
||||
}
|
||||
}
|
||||
/// @throw anything std::ostream::operator<< may throw.
|
||||
inline std::ostream &operator<<(std::ostream &os, const PropertyValue &value) {
|
||||
switch (value.type()) {
|
||||
case PropertyValue::Type::Null:
|
||||
@ -221,7 +240,7 @@ inline std::ostream &operator<<(std::ostream &os, const PropertyValue &value) {
|
||||
|
||||
// comparison
|
||||
inline bool operator==(const PropertyValue &first,
|
||||
const PropertyValue &second) {
|
||||
const PropertyValue &second) noexcept {
|
||||
if (first.type() != second.type()) return false;
|
||||
switch (first.type()) {
|
||||
case PropertyValue::Type::Null:
|
||||
@ -240,7 +259,9 @@ inline bool operator==(const PropertyValue &first,
|
||||
return first.ValueMap() == second.ValueMap();
|
||||
}
|
||||
}
|
||||
inline bool operator<(const PropertyValue &first, const PropertyValue &second) {
|
||||
|
||||
inline bool operator<(const PropertyValue &first,
|
||||
const PropertyValue &second) noexcept {
|
||||
if (first.type() != second.type()) return first.type() < second.type();
|
||||
switch (first.type()) {
|
||||
case PropertyValue::Type::Null:
|
||||
|
@ -140,6 +140,8 @@ class VerticesIterable final {
|
||||
|
||||
VertexAccessor operator*() const;
|
||||
|
||||
/// @throw std::bad_alloc raised in
|
||||
/// LabelPropertyIndex::Iterable::Iterator::operator++
|
||||
Iterator &operator++();
|
||||
|
||||
bool operator==(const Iterator &other) const;
|
||||
@ -152,6 +154,8 @@ class VerticesIterable final {
|
||||
|
||||
class Storage final {
|
||||
public:
|
||||
/// @throw std::system_error
|
||||
/// @throw std::bad_alloc
|
||||
explicit Storage(StorageGcConfig gc_config = DefaultGcConfig);
|
||||
|
||||
~Storage();
|
||||
@ -174,6 +178,7 @@ class Storage final {
|
||||
|
||||
~Accessor();
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
VertexAccessor CreateVertex();
|
||||
|
||||
std::optional<VertexAccessor> FindVertex(Gid gid, View view);
|
||||
@ -184,13 +189,17 @@ class Storage final {
|
||||
&storage_->indices_));
|
||||
}
|
||||
|
||||
/// @throw std::bad_alloc raised in Index::Vertices
|
||||
VerticesIterable Vertices(LabelId label, View view);
|
||||
|
||||
/// @throw std::bad_alloc raised in Index::Vertices
|
||||
VerticesIterable Vertices(LabelId label, PropertyId property, View view);
|
||||
|
||||
/// @throw std::bad_alloc raised in Index::Vertices
|
||||
VerticesIterable Vertices(LabelId label, PropertyId property,
|
||||
const PropertyValue &value, View view);
|
||||
|
||||
/// @throw std::bad_alloc raised in Index::Vertices
|
||||
VerticesIterable Vertices(
|
||||
LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
@ -236,21 +245,30 @@ class Storage final {
|
||||
label, property, lower, upper);
|
||||
}
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
Result<bool> DeleteVertex(VertexAccessor *vertex);
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
Result<bool> DetachDeleteVertex(VertexAccessor *vertex);
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to,
|
||||
EdgeTypeId edge_type);
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
Result<bool> DeleteEdge(EdgeAccessor *edge);
|
||||
|
||||
const std::string &LabelToName(LabelId label) const;
|
||||
const std::string &PropertyToName(PropertyId property) const;
|
||||
const std::string &EdgeTypeToName(EdgeTypeId edge_type) const;
|
||||
|
||||
/// @throw std::bad_alloc if unable to insert a new mapping
|
||||
LabelId NameToLabel(const std::string &name);
|
||||
|
||||
/// @throw std::bad_alloc if unable to insert a new mapping
|
||||
PropertyId NameToProperty(const std::string &name);
|
||||
|
||||
/// @throw std::bad_alloc if unable to insert a new mapping
|
||||
EdgeTypeId NameToEdgeType(const std::string &name);
|
||||
|
||||
void AdvanceCommand();
|
||||
@ -258,8 +276,10 @@ class Storage final {
|
||||
/// Commit returns `ExistenceConstraintViolation` if the changes made by
|
||||
/// this transaction violate an existence constraint. In that case the
|
||||
/// transaction is automatically aborted. Otherwise, nullopt is returned.
|
||||
/// @throw std::bad_alloc
|
||||
[[nodiscard]] std::optional<ExistenceConstraintViolation> Commit();
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
void Abort();
|
||||
|
||||
private:
|
||||
@ -276,10 +296,16 @@ class Storage final {
|
||||
const std::string &PropertyToName(PropertyId property) const;
|
||||
const std::string &EdgeTypeToName(EdgeTypeId edge_type) const;
|
||||
|
||||
/// @throw std::bad_alloc if unable to insert a new mapping
|
||||
LabelId NameToLabel(const std::string &name);
|
||||
|
||||
/// @throw std::bad_alloc if unable to insert a new mapping
|
||||
PropertyId NameToProperty(const std::string &name);
|
||||
|
||||
/// @throw std::bad_alloc if unable to insert a new mapping
|
||||
EdgeTypeId NameToEdgeType(const std::string &name);
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
bool CreateIndex(LabelId label, PropertyId property) {
|
||||
std::unique_lock<utils::RWLock> storage_guard(main_lock_);
|
||||
return indices_.label_property_index.CreateIndex(label, property,
|
||||
@ -317,6 +343,8 @@ class Storage final {
|
||||
}
|
||||
|
||||
private:
|
||||
/// @throw std::system_error
|
||||
/// @throw std::bad_alloc
|
||||
void CollectGarbage();
|
||||
|
||||
// Main storage lock.
|
||||
|
@ -36,6 +36,7 @@ struct Transaction {
|
||||
|
||||
~Transaction() {}
|
||||
|
||||
/// @throw std::bad_alloc if failed to create the `commit_timestamp`
|
||||
void EnsureCommitTimestampExists() {
|
||||
if (commit_timestamp != nullptr) return;
|
||||
commit_timestamp = std::make_unique<std::atomic<uint64_t>>(transaction_id);
|
||||
|
@ -26,12 +26,21 @@ class VertexAccessor final {
|
||||
Transaction *transaction,
|
||||
Indices *indices, View view);
|
||||
|
||||
/// Add a label and return `true` if insertion took place.
|
||||
/// `false` is returned if the label already existed.
|
||||
/// @throw std::bad_alloc
|
||||
Result<bool> AddLabel(LabelId label);
|
||||
|
||||
/// Remove a label and return `true` if deletion took place.
|
||||
/// `false` is returned if the vertex did not have a label already.
|
||||
/// @throw std::bad_alloc
|
||||
Result<bool> RemoveLabel(LabelId label);
|
||||
|
||||
Result<bool> HasLabel(LabelId label, View view) const;
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error if the resulting vector exceeds
|
||||
/// std::vector::max_size().
|
||||
Result<std::vector<LabelId>> Labels(View view) const;
|
||||
|
||||
/// Set a property value and return `true` if insertion took place.
|
||||
@ -39,13 +48,22 @@ class VertexAccessor final {
|
||||
/// @throw std::bad_alloc
|
||||
Result<bool> SetProperty(PropertyId property, const PropertyValue &value);
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
Result<PropertyValue> GetProperty(PropertyId property, View view) const;
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
Result<std::map<PropertyId, PropertyValue>> Properties(View view) const;
|
||||
|
||||
// TODO: Add API for obtaining edges filtered by destination VertexAccessor
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error if the resulting vector exceeds
|
||||
/// std::vector::max_size().
|
||||
Result<std::vector<EdgeAccessor>> InEdges(
|
||||
const std::vector<EdgeTypeId> &edge_types, View view) const;
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error if the resulting vector exceeds
|
||||
/// std::vector::max_size().
|
||||
Result<std::vector<EdgeAccessor>> OutEdges(
|
||||
const std::vector<EdgeTypeId> &edge_types, View view) const;
|
||||
|
||||
@ -73,7 +91,7 @@ class VertexAccessor final {
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<storage::VertexAccessor> {
|
||||
size_t operator()(const storage::VertexAccessor &v) const {
|
||||
size_t operator()(const storage::VertexAccessor &v) const noexcept {
|
||||
return v.Gid().AsUint();
|
||||
}
|
||||
};
|
||||
|
@ -26,6 +26,8 @@ class Scheduler {
|
||||
* @param f - Function
|
||||
* @Tparam TRep underlying arithmetic type in duration
|
||||
* @Tparam TPeriod duration in seconds between two ticks
|
||||
* @throw std::system_error if thread could not be started.
|
||||
* @throw std::bad_alloc
|
||||
*/
|
||||
template <typename TRep, typename TPeriod>
|
||||
void Run(const std::string &service_name,
|
||||
@ -67,6 +69,7 @@ class Scheduler {
|
||||
/**
|
||||
* @brief Stops the thread execution. This is a blocking call and may take as
|
||||
* much time as one call to the function given previously to Run takes.
|
||||
* @throw std::system_error
|
||||
*/
|
||||
void Stop() {
|
||||
is_working_.store(false);
|
||||
|
Loading…
Reference in New Issue
Block a user