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
19CliListener* GlobalCliComm::addListener(std::unique_ptr<CliListener> listener)
20{
21 // can be called from any thread
22 std::scoped_lock 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 return p;
31}
32
33std::unique_ptr<CliListener> GlobalCliComm::removeListener(CliListener& listener)
34{
35 // can be called from any thread
36 std::scoped_lock lock(mutex);
37 auto it = rfind_unguarded(listeners, &listener,
38 [](const auto& ptr) { return ptr.get(); });
39 auto result = std::move(*it);
40 move_pop_back(listeners, it);
41 return result;
42}
43
45{
46 assert(!allowExternalCommands); // should only be called once
47 allowExternalCommands = true;
48 for (auto& listener : listeners) {
49 if (auto* conn = dynamic_cast<CliConnection*>(listener.get())) {
50 conn->start();
51 }
52 }
53}
54
55void GlobalCliComm::log(LogLevel level, std::string_view message, float fraction)
56{
57 assert(Thread::isMainThread());
58
59 if (delivering) {
60 // Don't allow recursive calls, this would hang while trying to
61 // acquire the mutex below. But also when we would change
62 // this to a recursive-mutex, this could result in an infinite
63 // loop.
64 // One example of a recursive invocation is when something goes
65 // wrong in the Tcl proc attached to message_callback (e.g. the
66 // font used to display the message could not be loaded).
67 std::cerr << "Recursive cliComm message: " << message << '\n';
68 return;
69 }
70 ScopedAssign sa(delivering, true);
71
72 std::scoped_lock lock(mutex);
73 if (!listeners.empty()) {
74 for (auto& l : listeners) {
75 l->log(level, message, fraction);
76 }
77 } else {
78 // don't let the message get lost
79 std::cerr << message;
80 if (level == PROGRESS && fraction >= 0.0f) {
81 std::cerr << "... " << int(100.0f * fraction) << '%';
82 }
83 std::cerr << '\n';
84 }
85}
86
87void GlobalCliComm::update(UpdateType type, std::string_view name, std::string_view value)
88{
89 assert(type < NUM_UPDATES);
90 updateHelper(type, {}, name, value);
91}
92
93void GlobalCliComm::updateFiltered(UpdateType type, std::string_view name, std::string_view value)
94{
95 assert(type < NUM_UPDATES);
96 if (auto [it, inserted] = prevValues[type].try_emplace(name, value);
97 !inserted) { // was already present ..
98 if (it->second == value) {
99 return; // .. with the same value
100 } else {
101 it->second = value; // .. but with a different value
102 }
103 }
104 updateHelper(type, {}, name, value);
105}
106
107void GlobalCliComm::updateHelper(UpdateType type, std::string_view machine,
108 std::string_view name, std::string_view value)
109{
110 assert(Thread::isMainThread());
111 std::scoped_lock lock(mutex);
112 for (auto& l : listeners) {
113 l->update(type, machine, name, value);
114 }
115}
116
117} // namespace openmsx
Assign new value to some variable and restore the original value when this object goes out of scope.
CliListener * addListener(std::unique_ptr< CliListener > listener)
void log(LogLevel level, std::string_view message, float fraction) override
Log a message with a certain priority level.
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:11
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition stl.hh:134
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:109