Skip to content
This repository has been archived by the owner on Sep 4, 2021. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #4 from nemomobile/staging
tryLock, proper testing and small fixes
  • Loading branch information
Denis Zalevskiy committed Oct 29, 2014
2 parents 022b7ac + 4ea6aac commit 050097a
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 11 deletions.
9 changes: 5 additions & 4 deletions include/qtaround/os.hpp
Expand Up @@ -247,15 +247,16 @@ class FileLock
public:
FileLock(LockFileHandle &&from) : lock_(std::move(from)) {}
//lock_ is movable only
void unlock() { lock_.reset(); }
private:
LockFileHandle lock_;
};

template <typename OnLocked>
LockFileHandle tryLock(LockFileHandle &&locker, OnLocked fn)
LockFileHandle tryLock(LockFileHandle &&locker, OnLocked fn, int timeout = 0)
{
if (locker) {
if (locker->tryLock()) {
if (locker->tryLock(timeout)) {
fn(FileLock(std::move(locker)));
return nullptr;
}
Expand All @@ -264,10 +265,10 @@ LockFileHandle tryLock(LockFileHandle &&locker, OnLocked fn)
}

template <typename OnLocked>
LockFileHandle tryLock(QString const &fileName, OnLocked fn)
LockFileHandle tryLock(QString const &fileName, OnLocked fn, int timeout = 0)
{
LockFileHandle h{new QLockFile(fileName)};
return tryLock(std::move(h), fn);
return tryLock(std::move(h), fn, timeout);
}

}}
Expand Down
10 changes: 9 additions & 1 deletion include/qtaround/subprocess.hpp
Expand Up @@ -49,7 +49,7 @@ class Process : public QObject

int rc() const
{
return is_error() ? -1111 : ps->exitCode();
return is_error() ? 254 : ps->exitCode();
}

qint64 write(QString const &data)
Expand Down Expand Up @@ -112,6 +112,14 @@ static inline int check_call(QString const &cmd, QStringList const &args)
return check_call(cmd, args, QVariantMap());
}

static inline Process start
(QString const &name, QStringList const &args = QStringList{})
{
Process ps;
ps.start(name, args);
return std::move(ps);
}

}}

#ifdef QTAROUND_NO_NS
Expand Down
1 change: 1 addition & 0 deletions rpm/qtaround.spec
Expand Up @@ -48,6 +48,7 @@ License: LGPLv2.1
Group: System Environment/Libraries
Requires: %{name} = %{version}-%{release}
Requires: %{name}-dbus = %{version}-%{release}
Requires: btrfs-progs
%description tests
%summary

Expand Down
24 changes: 21 additions & 3 deletions src/os.cpp
Expand Up @@ -307,9 +307,11 @@ class BtrFs {

QVariantMap df()
{
QStringList cmd_options = {"-c", "btrfs fi df " + singleQuoted(path)};
auto out = str(subprocess::check_output("sh", cmd_options));
auto data = filterEmpty(out.trimmed().split("\n"));
bool isOk;
QStringList data;
std::tie(isOk, data) = fiDf();
if (!isOk)
return QVariantMap{};

auto split_colon = [](QString const &l) { return l.split(":"); };
auto name_fields = util::map<QStringList>(split_colon, data);
Expand All @@ -335,6 +337,8 @@ class BtrFs {
auto kb = bs / kb_bytes;
auto total = kb * b;
auto info = df();
// no btrfs exec
if (info.isEmpty()) return total;

auto used = util::map<double>([](QString const &, QVariant const &v) {
auto get_used = [](QString const &k, QVariant const &v) {
Expand All @@ -347,6 +351,20 @@ class BtrFs {
}

private:

std::tuple<bool, QStringList> fiDf()
{
QStringList cmd_options = {"-c", "btrfs fi df " + singleQuoted(path)};
auto ps = subprocess::start("sh", cmd_options);
ps.wait(-1);
if (!ps.rc()) {
auto out = str(ps.stdout());
return std::make_tuple(true, filterEmpty(out.trimmed().split("\n")));
} else {
return std::make_tuple(false, QStringList());
}
}

static const size_t kb_bytes = 1024;
QString path;
};
Expand Down
163 changes: 162 additions & 1 deletion tests/os.cpp
Expand Up @@ -3,6 +3,11 @@
#include "tests_common.hpp"
#include <tut/tut.hpp>
#include <cor/util.hpp>
#include <cor/os.hpp>

#include <atomic>
#include <sys/types.h>
#include <sys/wait.h>

namespace os = qtaround::os;
namespace error = qtaround::error;
Expand Down Expand Up @@ -85,7 +90,8 @@ enum test_ids {
tid_mountpoint,
tid_stat,
tid_diskFree,
tid_du
tid_du,
tid_open_lock
};

#define DQ "\""
Expand Down Expand Up @@ -415,4 +421,159 @@ void object::test<tid_du>()
}
}

template<> template<>
void object::test<tid_open_lock>()
{
static const auto name = os::getTemp("qtaround-test-lock");

// non-reenterable
static std::atomic<char> poll_err_count(ATOMIC_VAR_INIT(0));

using cor::posix::Pipe;
namespace os = qtaround::os;

// parent sends cookie to the forked child and expects incremented
// cookie back
auto to_child = Pipe::create(), from_child = Pipe::create();

auto child = [&]() {
auto in = cor::use<Pipe::Read>(to_child);
auto out = cor::use<Pipe::Write>(from_child);
int cookie = 5;

auto pollIn = [&in, &cookie]() {
auto ev = cor::poll(in, POLLIN | POLLPRI, 1000 * 5);
if ((ev & (POLLHUP | POLLERR)) || !ev) {
++poll_err_count;
return false;
}
return cor::read(in, cookie) == sizeof(cookie);
};
auto write = [&out, &cookie]() {
return cor::write(out, cookie) == sizeof(cookie);
};

auto shouldNotLock = [&cookie]() {
bool is_locked = false;
auto got_unexpected_lock = [&is_locked](os::FileLock) {
is_locked = true;
};
auto res = os::tryLock(name, got_unexpected_lock);
if (!(!res || is_locked))
++cookie;
return true;
};

auto shouldLock = [&cookie, &write, &pollIn]() {
bool is_locked = false;
auto got_expected_lock = [&](os::FileLock) {
is_locked = true;
++cookie;
write();
// locked till parent allow to go
pollIn();
};
auto res = os::tryLock(name, got_expected_lock);
// if not locked parent should be notified /w the same cookie
if (!is_locked) {
write();
pollIn();
}
if (!res) ++cookie;
write();
};

if (pollIn()) shouldNotLock();
write();
if (pollIn()) shouldLock();
};

auto parent = [&]() {
auto in = cor::use<Pipe::Read>(from_child);
auto out = cor::use<Pipe::Write>(to_child);

auto delOnExit = cor::on_scope_exit([]() {
if (os::path::exists(name)) os::rm(name);
});

auto pollRead = [&in](std::string const &msg, int data) {
auto events = cor::poll(in, POLLIN | POLLPRI, 1000 * 5);
ensure_eq("Bad poll event:" + msg, events & (POLLHUP | POLLERR), 0);
ensure_ne("No reply:" + msg, events, 0);
int res = 0;
ensure_eq("Read:" + msg, cor::read(in, res), sizeof(res));
ensure_eq(msg, res, data);
return res;
};

auto lockChildCantLock = [&]() {
auto isLocked = false;
auto gotExpectedLock = [&isLocked, &out, &pollRead](os::FileLock) {
isLocked = true;
cor::write(out, 1);
pollRead("Child should not be able to lock", 2);
};
auto res = os::tryLock(name, gotExpectedLock);
ensure("Should get lock", isLocked);
ensure("Should get null locker", !res);
};

auto childLockParentLockLater = [&]() {
cor::write(out, 11);
pollRead("Child should be able to lock", 12);
auto shouldNotLock = []() {
auto gotUnexpectedLock = [](os::FileLock) {
fail("Parent should not be able to lock");
};
auto locker = os::tryLock(name, gotUnexpectedLock);
ensure("Parent should get locker", !!locker);
return std::move(locker);
};
auto locker = shouldNotLock();
cor::write(out, 21);
pollRead("Child should release lock, child locker should be null", 22);
ensure("Locker should be available", !!locker);

auto isLocked = false;
auto gotExpectedLock = [&isLocked](os::FileLock) {
isLocked = true;
};
locker = os::tryLock(std::move(locker), gotExpectedLock);
ensure("Should get lock", isLocked);
ensure("Should get null locker", !locker);
};

lockChildCantLock();
childLockParentLockLater();
};

auto rc = ::fork();
ensure_ne("Can't fork", rc, -1);

auto wait_on_exit = [] () {
int child_status = 255;
int counter = 1000 * 5;
int rc;
while (!(rc = ::waitpid(-1, &child_status, WNOHANG))) {
if (rc == -1) return 100;
if (!--counter) return 101;
::usleep(1000);
}

return child_status;
};

if (!rc)
child();
else {
try {
parent();
} catch (...) {
wait_on_exit();
throw;
}
ensure_eq("Bad child", wait_on_exit(), 0);
}
}

}
10 changes: 8 additions & 2 deletions tests/tests.xml.in
Expand Up @@ -4,8 +4,14 @@
<description>QtAround automatic tests</description>
<set name="unit-tests" feature="qtaround tests">
<description>Testing qtaround</description>
<case manual="false" name="unittests">
<step>cd @TESTS_DIR@ &amp;&amp; find -type f -name 'test_*' -exec {} \;</step>
<case manual="false" name="os">
<step>cd @TESTS_DIR@ &amp;&amp; ./test_os</step>
</case>
<case manual="false" name="dbus">
<step>cd @TESTS_DIR@ &amp;&amp; ./test_dbus</step>
</case>
<case manual="false" name="misc">
<step>cd @TESTS_DIR@ &amp;&amp; ./test_misc</step>
</case>
</set>
</suite>
Expand Down

0 comments on commit 050097a

Please sign in to comment.