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 
11 namespace openmsx {
12 
14 {
15  assert(Thread::isMainThread());
16  assert(!delivering);
17 }
18 
19 void 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 
32 std::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 
54 void 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 
82 void GlobalCliComm::update(UpdateType type, std::string_view name, std::string_view value)
83 {
84  assert(type < NUM_UPDATES);
85  if (auto [it, inserted] = prevValues[type].try_emplace(name, value);
86  !inserted) { // was already present ..
87  if (it->second == value) {
88  return; // .. with the same value
89  } else {
90  it->second = value; // .. but with a different value
91  }
92  }
93  updateHelper(type, {}, name, value);
94 }
95 
96 void GlobalCliComm::updateHelper(UpdateType type, std::string_view machine,
97  std::string_view name, std::string_view value)
98 {
99  assert(Thread::isMainThread());
100  std::lock_guard<std::mutex> lock(mutex);
101  for (auto& l : listeners) {
102  l->update(type, machine, name, value);
103  }
104 }
105 
106 } // 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)
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: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