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_if_unguarded(listeners,
37  [&](const std::unique_ptr<CliListener>& ptr) {
38  return ptr.get() == &listener; });
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 
55 void GlobalCliComm::log(LogLevel level, std::string_view message)
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::lock_guard<std::mutex> lock(mutex);
73  if (!listeners.empty()) {
74  for (auto& l : listeners) {
75  l->log(level, message);
76  }
77  } else {
78  // don't let the message get lost
79  std::cerr << message << '\n';
80  }
81 }
82 
83 void GlobalCliComm::update(UpdateType type, std::string_view name, std::string_view value)
84 {
85  assert(type < NUM_UPDATES);
86  if (auto v = lookup(prevValues[type], name)) {
87  if (*v == value) {
88  return;
89  }
90  *v = value;
91  } else {
92  prevValues[type].emplace_noDuplicateCheck(name, value);
93  }
94  updateHelper(type, {}, name, value);
95 }
96 
97 void GlobalCliComm::updateHelper(UpdateType type, std::string_view machine,
98  std::string_view name, std::string_view value)
99 {
100  assert(Thread::isMainThread());
101  std::lock_guard<std::mutex> lock(mutex);
102  for (auto& l : listeners) {
103  l->update(type, machine, name, value);
104  }
105 }
106 
107 } // namespace openmsx
void addListener(std::unique_ptr< CliListener > listener)
const Value * lookup(const hash_map< Key, Value, Hasher, Equal > &map, const Key2 &key)
Definition: hash_map.hh:91
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:177
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void update(UpdateType type, std::string_view name, std::string_view value) override
iterator emplace_noDuplicateCheck(Args &&... args)
Definition: hash_set.hh:468
std::unique_ptr< CliListener > removeListener(CliListener &listener)
auto rfind_if_unguarded(RANGE &range, PRED pred)
Definition: stl.hh:160
void log(LogLevel level, std::string_view message) override
bool isMainThread()
Returns true when called from the main thread.
Definition: Thread.cc:15
Assign new value to some variable and restore the original value when this object goes out of scope...
Definition: ScopedAssign.hh:7