libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
libxr_system.cpp
1#include "libxr_system.hpp"
2
3#include <sys/ioctl.h>
4#include <sys/select.h>
5#include <sys/types.h>
6#include <termios.h>
7#include <unistd.h>
8
9#include <algorithm>
10#include <cstddef>
11
12#include "libxr_def.hpp"
13#include "libxr_rw.hpp"
14#include "libxr_type.hpp"
15#include "linux_timebase.hpp"
16#include "logger.hpp"
17#include "thread.hpp"
18#include "timer.hpp"
19
20struct timespec libxr_linux_start_time_spec; // NOLINT
21
22static LibXR::LinuxTimebase libxr_linux_timebase;
23
24// STDIO 线程不会退出,信号量须在进程退出期间保持有效。
25// The STDIO threads never exit, so the semaphores stay valid during process exit.
26static LibXR::Semaphore& stdo_sem = *new LibXR::Semaphore();
27static LibXR::Semaphore& stdi_space_sem = *new LibXR::Semaphore();
28static constexpr size_t host_stdio_queue_bytes = 4096;
29
30namespace
31{
32class StdioReadPort final : public LibXR::ReadPort
33{
34 public:
35 StdioReadPort(size_t size, LibXR::Semaphore& space_sem)
36 : ReadPort(size), space_sem_(space_sem)
37 {
38 }
39
40 protected:
41 void OnReadQueueSpaceAvailable(bool in_isr) override
42 {
43 space_sem_.PostFromCallback(in_isr);
44 }
45
46 private:
47 LibXR::Semaphore& space_sem_;
48};
49} // namespace
50
51void StdiThread(LibXR::ReadPort* read_port)
52{
53 static uint8_t read_buff[host_stdio_queue_bytes];
54
55 if (!isatty(STDIN_FILENO))
56 {
57 XR_LOG_WARN("STDIO.read_: stdin is not a TTY, parking thread forever");
58 while (true)
59 {
60 LibXR::Thread::Sleep(UINT32_MAX);
61 }
62 }
63
64 while (true)
65 {
66 fd_set rfds;
67 FD_ZERO(&rfds);
68 FD_SET(STDIN_FILENO, &rfds);
69
70 int ret = select(STDIN_FILENO + 1, &rfds, NULL, NULL, NULL);
71
72 if (ret > 0 && FD_ISSET(STDIN_FILENO, &rfds))
73 {
74 int ready = 0;
75 if (ioctl(STDIN_FILENO, FIONREAD, &ready) != -1 && ready > 0)
76 {
77 auto queue = read_port->GetReadQueue(false);
78 const size_t read_size = std::min(static_cast<size_t>(ready), queue.EmptySize());
79 if (read_size == 0U)
80 {
81 queue.Publish();
83 do
84 {
85 wait_result = stdi_space_sem.Wait(UINT32_MAX);
86 } while (wait_result == LibXR::ErrorCode::TIMEOUT);
87 REQUIRE(wait_result == LibXR::ErrorCode::OK);
88 continue;
89 }
90
91 auto size = fread(read_buff, sizeof(char), read_size, stdin);
92 if (size < 1)
93 {
94 queue.Publish();
95 continue;
96 }
97 [[maybe_unused]] const auto push_batch_result = queue.PushBatch(read_buff, size);
98 DEV_ASSERT(push_batch_result == LibXR::ErrorCode::OK);
99 queue.Publish();
100 }
101 }
102 }
103}
104
105void StdoThread(LibXR::WritePort* write_port)
106{
107 while (true)
108 {
109 if (stdo_sem.Wait() == LibXR::ErrorCode::OK)
110 {
111 for (;;)
112 {
113 bool failed = false;
114 {
115 auto queue = write_port->GetWriteQueue(false);
116 if (queue.Empty())
117 {
118 break;
119 }
120
121 const size_t accepted = queue.PopWithWriter(
122 queue.AvailableSize(),
123 [&failed](const uint8_t* first, size_t first_size, const uint8_t* second,
124 size_t second_size) -> size_t
125 {
126 const size_t first_written =
127 fwrite(first, sizeof(char), first_size, stdout);
128 if (first_written != first_size)
129 {
130 failed = true;
131 return first_written;
132 }
133
134 const size_t second_written =
135 fwrite(second, sizeof(char), second_size, stdout);
136 if (second_written != second_size)
137 {
138 failed = true;
139 }
140 return first_written + second_written;
141 });
142 UNUSED(accepted);
143 UNUSED(fflush(stdout));
144 }
145
146 if (failed)
147 {
148 auto failed_queue = write_port->GetWriteQueue(false);
149 if (!failed_queue.Empty())
150 {
151 failed_queue.FailFront(LibXR::ErrorCode::FAILED);
152 }
153 }
154 }
155 }
156 }
157}
158
159void LibXR::PlatformInit(uint32_t timer_pri, uint32_t timer_stack_depth)
160{
161 LibXR::Timer::priority_ = static_cast<LibXR::Thread::Priority>(timer_pri);
162 LibXR::Timer::stack_depth_ = timer_stack_depth;
163 auto write_fun = [](WritePort& port, bool)
164 {
165 UNUSED(port);
166 stdo_sem.Post();
167 };
168
169 LibXR::STDIO::write_ = new LibXR::WritePort(32, host_stdio_queue_bytes);
170
171 *LibXR::STDIO::write_ = write_fun;
172
173 LibXR::STDIO::read_ = new StdioReadPort(host_stdio_queue_bytes, stdi_space_sem);
174
175 struct termios tty = {};
176 if (isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &tty) == 0)
177 {
178 // 关闭规范输入模式和回显 / Disable canonical input and echo.
179 tty.c_lflag &= ~(ICANON | ECHO);
180 tcsetattr(STDIN_FILENO, TCSANOW, &tty); // 立即生效 / Apply immediately.
181 }
182
183 LibXR::Thread stdi_thread, stdo_thread;
184 stdi_thread.Create<LibXR::ReadPort*>(LibXR::STDIO::read_, StdiThread, "STDIO.read_",
186
187 stdo_thread.Create<LibXR::WritePort*>(LibXR::STDIO::write_, StdoThread, "STDIO.write_",
189}
Linux 时间基准实现 / Linux timebase implementation.
基于 SPSC 字节队列的读端口 / Read endpoint backed by an SPSC byte queue.
Definition read_port.hpp:30
ReadQueue GetReadQueue(bool in_isr=false)
获取后端接收入队接口 / Obtain the backend RX producer interface.
Definition read_port.cpp:14
static ReadPort * read_
读取端口 / Read port instance.
Definition stdio.hpp:25
static WritePort * write_
写入端口 / Write port instance.
Definition stdio.hpp:26
信号量类,实现线程同步机制 Semaphore class implementing thread synchronization
Definition semaphore.hpp:23
void Post()
释放(增加)信号量 Releases (increments) the semaphore
Definition semaphore.cpp:43
ErrorCode Wait(uint32_t timeout=UINT32_MAX)
等待(减少)信号量 Waits (decrements) the semaphore
Definition semaphore.cpp:49
线程管理类,封装 POSIX 线程创建和调度 Thread management class encapsulating POSIX thread creation and scheduling
Definition thread.hpp:19
Priority
线程优先级枚举 Enumeration for thread priorities
Definition thread.hpp:26
@ MEDIUM
中等优先级 / Medium priority
void Create(ArgType arg, void(*function)(ArgType arg), const char *name, size_t stack_depth, Thread::Priority priority)
创建新线程 Creates a new thread
Definition thread.hpp:74
static void Sleep(uint32_t milliseconds)
让线程进入休眠状态 Puts the thread to sleep
Definition thread.cpp:15
static uint32_t stack_depth_
线程栈深度 / Thread stack depth
Definition timer.hpp:171
static LibXR::Thread::Priority priority_
线程优先级 / Thread priority
Definition timer.hpp:169
size_t PopWithWriter(size_t limit, Writer &&writer)
通过回调取走队头前缀 / Consume a front prefix through a callback.
基于 SPSC 字节队列的写端口 / Write endpoint backed by an SPSC byte queue.
WriteQueue GetWriteQueue(bool in_isr=false)
获取已提交队头的出队接口 / Obtain a consumer interface for the released front.
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ TIMEOUT
超时 / Timeout
@ FAILED
操作失败 / Operation failed
@ OK
操作成功 / Operation successful
void PlatformInit(uint32_t timer_pri=2, uint32_t timer_stack_depth=65536)
平台初始化函数 Platform initialization function