OpenMPTL - Helper Library
C++ Microprocessor Template Library
terminal_sim.hpp
Go to the documentation of this file.
1 /*
2  * OpenMPTL - C++ Microprocessor Template Library
3  *
4  * Copyright (C) 2012-2017 Axel Burri <axel@tty0.ch>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #ifndef TERMINAL_SIM_HPP_INCLUDED
22 #define TERMINAL_SIM_HPP_INCLUDED
23 
24 #include <simulation.hpp>
25 #include <iostream>
26 #include <thread>
27 #include <atomic>
28 #include <chrono>
29 #include <ratio>
30 #include <poll.h>
31 
32 namespace mptl { namespace sim {
33 
34 /**
35  * Simulate a terminal (Tp) on stdin/stdout, by starting rx/tx threads
36  * directly manipulating the fifo of Tp::stream_device_type.
37  *
38  * For use in reg_reaction::react().
39  *
40  * NOTE: The threads use std::cin and std::cout without mutexes!
41  */
42 template<typename Tp>
44 {
45  reg_reaction const & reaction;
46 
47  static std::atomic<bool> terminal_rx_thread_terminate;
48  static std::atomic<bool> terminal_tx_thread_terminate;
49 
50  /**
51  * Poll stdin, and feed result into stream_device_type::rx_fifo.
52  *
53  * NOTE: This thread uses terminal::istream without mutexes!
54  */
55  static void terminal_rx_thread() {
56  char c;
57  pollfd cinfd[1];
58  cinfd[0].fd = fileno(stdin);
59  cinfd[0].events = POLLIN;
60 
61  // std::cout << "*** terminal_rx_thread() running" << std::endl;
62  while(!terminal_rx_thread_terminate)
63  {
64  if(poll(cinfd, 1, 0))
65  {
66  c = std::cin.get();
67  // std::cout << '<' << +c << '>' << std::endl;
68  if(c == 10) c = 13; // convert LF into CR (hacky...)
69 
70  /* feed rx_fifo, will pe polled in terminal.process_input() */
71  Tp::stream_device_type::rx_fifo.push(c);
72  }
73  SIM_RELAX; // sleep a bit (don't eat up all cpu power)
74  }
75  // std::cout << "*** terminal_rx_thread() terminated" << std::endl;
76  }
77 
78  /**
79  * Hook into stream_device_type::tx_fifo, and print output to stdout.
80  *
81  * NOTE: This thread uses std::cout without mutexes!
82  */
83  static void terminal_tx_thread() {
84  // std::cout << "*** terminal_tx_thread() running" << std::endl;
85  while(!terminal_tx_thread_terminate)
86  {
87  char c;
88  while(Tp::stream_device_type::tx_fifo.pop(c)) {
89  std::cout << c;
90  }
91  std::cout << std::flush;
92  SIM_RELAX; // sleep a bit (don't eat up all cpu power)
93  }
94  // std::cout << "*** terminal_tx_thread() terminated" << std::endl;
95  }
96 
97 public:
98 
99  stdio_terminal(reg_reaction const & r): reaction(r) { }
100 
101  template<
102  typename rx_trigger_regmask_type,
103  typename tx_trigger_regmask_type
104  >
105  void react(void)
106  {
107  /* start/stop terminal rx thread on rx_trigger_regmask_type */
108  if(reaction.bits_set< rx_trigger_regmask_type >()) {
109  reaction.info("terminal: starting RX thread");
110  terminal_rx_thread_terminate = false;
111  std::thread(terminal_rx_thread).detach();
112  }
113  else if(reaction.bits_cleared< rx_trigger_regmask_type >()) {
114  reaction.info("terminal: stopping RX thread");
115  terminal_rx_thread_terminate = true;
116  }
117 
118  /* start/stop terminal tx thread on tx_trigger_regmask_type */
119  if(reaction.bits_set< tx_trigger_regmask_type >()) {
120  reaction.info("terminal: starting TX thread");
121  terminal_tx_thread_terminate = false;
122  std::thread(terminal_tx_thread).detach();
123  }
124  else if(reaction.bits_cleared< tx_trigger_regmask_type >()) {
125  reaction.info("terminal: stopping TX thread");
126  terminal_tx_thread_terminate = true;
127  }
128  }
129 };
130 
131 template<typename Tp> std::atomic<bool> stdio_terminal<Tp>::terminal_rx_thread_terminate;
132 template<typename Tp> std::atomic<bool> stdio_terminal<Tp>::terminal_tx_thread_terminate;
133 
134 } } // namespace mptl::sim
135 
136 #endif // TERMINAL_SIM_HPP_INCLUDED
void info(const std::string &str) const
bool bits_set(void) const
void react(void)
Definition: terminal_sim.hpp:105
ostream< Tp > & flush(ostream< Tp > &st)
manipulator, flushes the output stream
Definition: poorman_ostream.hpp:69
#define SIM_RELAX
Simulate a terminal (Tp) on stdin/stdout, by starting rx/tx threads directly manipulating the fifo of...
Definition: terminal_sim.hpp:43
stdio_terminal(reg_reaction const &r)
Definition: terminal_sim.hpp:99
bool bits_cleared(void) const