Skip to content

Commit 9b944b2

Browse files
committed
Obtaining and releasing locks
Change-Id: Ib37d2af387f7d1b219432fc7d917d25afabcdf50 Bug: sysrepo#19
1 parent 14693a6 commit 9b944b2

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed

include/sysrepo-cpp/Session.hpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,29 @@ class Session {
161161
std::shared_ptr<sr_conn_ctx_s> m_conn;
162162
std::shared_ptr<sr_session_ctx_s> m_sess;
163163
};
164+
165+
/**
166+
* @brief Lock the current datastore, or a specified module in a datastore
167+
*
168+
* Lock release is controlled through [RAII](https://en.cppreference.com/w/cpp/language/raii).
169+
*/
170+
class Lock {
171+
public:
172+
/** @brief Obtain a lock
173+
*
174+
* Wraps `sr_lock`.
175+
*/
176+
explicit Lock(Session session, std::optional<std::string> module = std::nullopt, std::optional<std::chrono::milliseconds> timeout = std::nullopt);
177+
/** @brief Release the lock
178+
*
179+
* Wraps `sr_unlock`.
180+
*/
181+
~Lock();
182+
183+
Lock& operator=(const Lock& other) = delete;
184+
Lock(const Lock& other) = delete;
185+
private:
186+
Session m_session;
187+
std::optional<std::string> m_module;
188+
};
164189
}

src/Session.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,20 @@ uint32_t Session::getId() const
695695
return sr_session_get_id(m_sess.get());
696696
}
697697

698+
Lock::Lock(Session session, std::optional<std::string> module, std::optional<std::chrono::milliseconds> timeout)
699+
: m_session(session)
700+
, m_module(module)
701+
{
702+
auto res = sr_lock(getRawSession(m_session), module ? module->c_str() : nullptr, timeout ? timeout->count() : 0);
703+
throwIfError(res, "Cannot lock session", getRawSession(m_session));
704+
}
705+
706+
Lock::~Lock()
707+
{
708+
auto res = sr_unlock(getRawSession(m_session), m_module ? m_module->c_str() : nullptr);
709+
throwIfError(res, "Cannot unlock session", getRawSession(m_session));
710+
}
711+
698712
sr_session_ctx_s* getRawSession(Session sess)
699713
{
700714
return sess.m_sess.get();

tests/session.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <sysrepo-cpp/utils/utils.hpp>
1313
#include <sysrepo-cpp/utils/exception.hpp>
1414

15+
using namespace std::literals;
16+
1517
TEST_CASE("session")
1618
{
1719
sysrepo::setLogLevelStderr(sysrepo::LogLevel::Information);
@@ -219,4 +221,49 @@ TEST_CASE("session")
219221
REQUIRE(sess.getId() == sess.getId());
220222
REQUIRE(sess.getId() != conn->sessionStart().getId());
221223
}
224+
225+
DOCTEST_SUBCASE("locking")
226+
{
227+
auto sid = sess.getId();
228+
229+
{
230+
// L1 will be released at the scope exit
231+
auto l1 = sysrepo::Lock{sess};
232+
const auto start = std::chrono::steady_clock::now();
233+
try {
234+
// even though we provide a timeout, an attempt to lock by the same session is detected immediately
235+
auto l2 = sysrepo::Lock{sess, std::nullopt, 500ms};
236+
FAIL("should have thrown (immediately)");
237+
} catch (sysrepo::ErrorWithCode& e) {
238+
REQUIRE(e.code() == sysrepo::ErrorCode::Locked);
239+
std::string msg = e.what();
240+
REQUIRE(msg.find("already locked by this session " + std::to_string(sid)) != std::string::npos);
241+
}
242+
auto shouldBeImmediate = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
243+
REQUIRE(shouldBeImmediate < 100);
244+
}
245+
{
246+
// ensure that L1 was released
247+
sysrepo::Lock l3{sess};
248+
}
249+
{
250+
sysrepo::Lock l4{sess, std::nullopt};
251+
const auto start = std::chrono::steady_clock::now();
252+
try {
253+
// locking through an unrelated session sleeps
254+
sysrepo::Lock{conn->sessionStart(), std::nullopt, 500ms};
255+
FAIL("should have thrown (after a timeout)");
256+
} catch (sysrepo::ErrorWithCode& e) {
257+
REQUIRE(e.code() == sysrepo::ErrorCode::Locked);
258+
std::string msg = e.what();
259+
REQUIRE(msg.find("is DS-locked by session " + std::to_string(sid)) != std::string::npos);
260+
}
261+
auto processingMS = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
262+
REQUIRE(processingMS >= 500);
263+
}
264+
{
265+
sysrepo::Lock m1_lock{sess, "test_module"};
266+
sysrepo::Lock m2_lock{sess, "ietf-netconf-acm"};
267+
}
268+
}
222269
}

0 commit comments

Comments
 (0)