openMSX
GlobalCliComm.cc
Go to the documentation of this file.
1#include "GlobalCliComm.hh"
2#include "CliListener.hh"
3#include "CliConnection.hh"
4#include "Thread.hh"
5#include "ScopedAssign.hh"
6#include "stl.hh"
7#include <cassert>
8#include <iostream>
9#include <utility>
10
11namespace openmsx {
12
14{
15 assert(Thread::isMainThread());
16 assert(!delivering);
17}
18
19void GlobalCliComm::addListener(std::unique_ptr<CliListener> listener)
20{
21 // can be called from any thread
22 std::lock_guard<std::mutex> lock(mutex);
23 auto* p = listener.get();
24 listeners.push_back(std::move(listener));
25 if (allowExternalCommands) {
26 if (auto* conn = dynamic_cast<CliConnection*>(p)) {
27 conn->start();
28 }
29 }
30}
31
32std::unique_ptr<CliListener> GlobalCliComm::removeListener(CliListener& listener)
33{
34 // can be called from any thread
35 std::lock_guard<std::mutex> lock(mutex);
36 auto it = rfind_unguarded(listeners, &listener,
37 [](const auto& ptr) { return ptr.get(); });
38 auto result = std::move(*it);
39 move_pop_back(listeners, it);
40 return result;
41}
42
44{
45 assert(!allowExternalCommands); // should only be called once
46 allowExternalCommands = true;
47 for (auto& listener : listeners) {
48 if (auto* conn = dynamic_cast<CliConnection*>(listener.get())) {
49 conn->start();
50 }
51 }
52}
53
54void GlobalCliComm::log(LogLevel level, std::string_view message)
55{
56 assert(Thread::isMainThread());
57
58 if (delivering) {
59 // Don't allow recursive calls, this would hang while trying to
60 // acquire the mutex below. But also when we would change
61 // this to a recursive-mutex, this could result in an infinite
62 // loop.
63 // One example of a recursive invocation is when something goes
64 // wrong in the Tcl proc attached to message_callback (e.g. the
65 // font used to display the message could not be loaded).
66 std::cerr << "Recursive cliComm message: " << message << '\n';
67 return;
68 }
69 ScopedAssign sa(delivering, true);
70
71 std::lock_guard<std::mutex> lock(mutex);
72 if (!listeners.empty()) {
73 for (auto& l : listeners) {
74 l->log(level, message);
75 }
76 } else {
77 // don't let the message get lost
78 std::cerr << message << '\n';
79 }
80}
81
82void GlobalCliComm::update(UpdateType type, std::string_view name, std::string_view value)
83{
84 assert(type < NUM_UPDATES);
85 updateHelper(type, {}, name, value);
86}
87
88void GlobalCliComm::updateFiltered(UpdateType type, std::string_view name, std::string_view value)
89{
90 assert(type < NUM_UPDATES);
91 if (auto [it, inserted] = prevValues[type].try_emplace(name, value);
92 !inserted) { // was already present ..
93 if (it->second == value) {
94 return; // .. with the same value
95 } else {
96 it->second = value; // .. but with a different value
97 }
98 }
99 updateHelper(type, {}, name, value);
100}
101
102void GlobalCliComm::updateHelper(UpdateType type, std::string_view machine,
103 std::string_view name, std::string_view value)
104{
105 assert(Thread::isMainThread());
106 std::lock_guard<std::mutex> lock(mutex);
107 for (auto& l : listeners) {
108 l->update(type, machine, name, value);
109 }
110}
111
112} // namespace openmsx
Assign new value to some variable and restore the original value when this object goes out of scope.
Definition: ScopedAssign.hh:8
void addListener(std::unique_ptr< CliListener > listener)
void log(LogLevel level, std::string_view message) override
void update(UpdateType type, std::string_view name, std::string_view value) override
std::unique_ptr< CliListener > removeListener(CliListener &listener)
void updateFiltered(UpdateType type, std::string_view name, std::string_view value) override
Same as update(), but checks that the value for type-name is the same as in the previous call.
bool isMainThread()
Returns true when called from the main thread.
Definition: Thread.cc:15
This file implemented 3 utility functions:
Definition: Autofire.cc:9
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:125
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition: stl.hh:100