File: catch2_tango_monitor.cpp

package info (click to toggle)
tango 10.0.2%2Bdfsg1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 89,936 kB
  • sloc: cpp: 201,786; sh: 1,645; python: 953; java: 800; perl: 467; javascript: 447; xml: 325; makefile: 272; sql: 72; ruby: 24
file content (107 lines) | stat: -rw-r--r-- 3,382 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <catch2/matchers/catch_matchers_string.hpp>
#include <tango/tango.h>

#include <memory>

#include <omnithread.h>

#include "utils/utils.h"

// A thread that holds a TangoMonitor for the duration it is running.
class HoldMonitorThread : public omni_thread
{
  public:
    HoldMonitorThread(const HoldMonitorThread &) = delete;
    HoldMonitorThread(HoldMonitorThread &&) = delete;
    HoldMonitorThread &operator=(const HoldMonitorThread &) = delete;
    HoldMonitorThread &operator=(HoldMonitorThread &&) = delete;

  private:
    static void stop_and_join(HoldMonitorThread *thread)
    {
        thread->m_done.post();
        thread->join(nullptr);
    }

  public:
    // `omni_thread` has a weird API where you are not supposed to call `delete`,
    // but instead `join`, however, we need an RAII wrapper around this so that
    // we clean up in the case a `REQUIRE_<X>(..)` fails (which will throw
    // an exception).  This unique_ptr will handle this for us.
    using UniquePtr = std::unique_ptr<HoldMonitorThread, decltype(&stop_and_join)>;

    static UniquePtr create(const char *name)
    {
        return {new HoldMonitorThread{name}, stop_and_join};
    }

    // Wait until the thread has grabbed the monitor.
    void wait_until_started()
    {
        m_ready.wait();
    }

    // When called after `wait_until_started` this should throw a timeout error.
    void grab_monitor()
    {
        m_monitor.get_monitor();
    }

  private:
    HoldMonitorThread(const char *name) :
        m_monitor{name},
        m_ready{0},
        m_done{0}
    {
        m_monitor.timeout(500); // 500 ms
        start_undetached();
    }

    ~HoldMonitorThread() override { }

    void *run_undetached(void *) override
    {
        Tango::AutoTangoMonitor guard{&m_monitor};

        m_ready.post();
        m_done.wait();

        return nullptr;
    }

    Tango::TangoMonitor m_monitor; // The monitor being held by the thread
    omni_semaphore m_ready;        // post()'d when by the thread when it has grabbed the lock
    omni_semaphore m_done;         // post()'d when by the test when it has finished
};

SCENARIO("TangoMonitor provides good error messages")
{
    constexpr const char *k_name = "a-descriptive-name";

    GIVEN("a named TangoMonitor lock by another thread")
    {
        auto thread = HoldMonitorThread::create(k_name);

        WHEN("we try to grab the monitor")
        {
            thread->wait_until_started();
            THEN("we timeout with a DevFailed that mentions the lock's name and the threads involved")
            {
                using Catch::Matchers::ContainsSubstring;
                using TangoTest::AnyErrorMatches;
                using TangoTest::DescriptionMatches;
                using TangoTest::Reason;

                std::string self = "Thread " + std::to_string(omni_thread::self()->id());
                std::string other = "held by thread " + std::to_string(thread->id());

                REQUIRE_THROWS_MATCHES(
                    thread->grab_monitor(),
                    Tango::DevFailed,
                    AnyErrorMatches(Reason(Tango::API_CommandTimedOut) &&
                                    DescriptionMatches(ContainsSubstring(k_name) && ContainsSubstring(self) &&
                                                       ContainsSubstring(other))));
            }
        }
    }
}