YSTest: Host.cpp 源文件

YSTest PreAlpha_b900_20201012 The YSLib Test Project

Host.cpp
浏览该文件的文档.
1 /*
2 © 2014-2020 FrankHB.
3
4 This file is part of the YSLib project, and may only be used,
5 modified, and distributed under the terms of the YSLib project
6 license, LICENSE.TXT. By continuing to use, modify, or distribute
7 this file you indicate that you have read the license and
8 understand and accept it fully.
9 */
10
30 #include "YCLib/YModules.h"
31 #include YFM_YCLib_Host // for make_observer, platform::CallNothrow;
32 #include YFM_YSLib_Core_YCoreUtilities // for YSLib, to_std_string,
33 // FetchCachedCommandResult, ystdex::underlying, FilterExceptions;
34 #include YFM_YCLib_NativeAPI // for YCL_TraceCallF_CAPI, ::sem_open,
35 // ::sem_close, ::sem_unlink, ::pipe, ToHandle, YCL_CallGlobal, isatty;
36 #include YFM_YCLib_FileIO // for MakePathStringW, YCL_Raise_SysE,
37 // MakePathString;
38 #include <stdlib.h> // for ::putenv, ::setenv;
39 #if YCL_Win32
40 # include <limits> // for std::numeric_limits;
41 # include YFM_Win32_YCLib_NLS // for CloseHandle, MBCSToMBCS;
42 # include YFM_Win32_YCLib_Consoles // for WConsole;
43 #endif
44 #if YF_Hosted
45 # include YFM_YSLib_Core_YConsole
46 # if !(YCL_Win32 || YCL_API_Has_semaphore_h)
47 # error "Unsupported platform found."
48 # endif
49 #endif
50
51 using namespace YSLib;
53 using YSLib::string;
55 using platform::string_view;
56
57 namespace platform
58 {
59
60 using namespace platform_ex;
61
62 } // namespace platform;
63
64 #if YF_Hosted
65 namespace platform_ex
66 {
67
68 Exception::Exception(std::error_code ec, const char* str, RecordLevel lv)
69 : system_error(ec, Nonnull(str)),
70 level(lv)
71 {}
72 Exception::Exception(std::error_code ec, const std::string& str, RecordLevel lv)
73 : system_error(ec, str),
74 level(lv)
75 {}
76 Exception::Exception(std::error_code ec, string_view sv, RecordLevel lv)
77 : Exception(ec, (Nonnull(sv.data()), to_std_string(sv)), lv)
78 {}
79 Exception::Exception(int ev, const std::error_category& ecat, const char* str,
80 RecordLevel lv)
81 : system_error(ev, ecat, Nonnull(str)),
82 level(lv)
83 {}
84 Exception::Exception(int ev, const std::error_category& ecat,
85 const std::string& str, RecordLevel lv)
86 : system_error(ev, ecat, str),
87 level(lv)
88 {}
89 Exception::Exception(int ev, const std::error_category& ecat, string_view sv,
90 RecordLevel lv)
91 : Exception(ev, ecat, (Nonnull(sv.data()), to_std_string(sv)), lv)
92 {}
93 ImplDeDtor(Exception)
94
95
96 #if YCL_Win32
97 void
98 HandleDelete::operator()(pointer h) const ynothrowv
99 {
100 YCL_TraceCallF_Win32(CloseHandle, h);
101 }
102 #endif
103
104
105 #if !YCL_Win32
106 void
107 Semaphore::Deleter::operator()(pointer p) const ynothrowv
108 {
109 YCL_TraceCallF_CAPI(::sem_close, static_cast<::sem_t*>(p));
110 if(Referent)
111 YCL_TraceCallF_CAPI(::sem_unlink, Referent->name.c_str());
112 }
113 #endif
114
115
116 Semaphore::Semaphore(const char* n, CountType init)
117 #if YCL_Win32
118 : Semaphore(platform::ucast(MakePathStringW(n).c_str()), init)
119 #else
120 : h_sem([this, n, init]{
121 using platform::OpenMode;
122 const auto create([=](OpenMode omode) ynothrow{
123 // TODO: Allow setting of permission mode.
124 return ystdex::retry_on_cond([](::sem_t* res) ynothrow{
125 return res == SEM_FAILED && errno == EINTR;
126 }, [=]() ynothrow{
127 // NOTE: Some implementations may force shared lock when
128 // %O_CREAT is specified, e.g. https://www.gnu.org/software/libc/manual/html_node/Open_002dtime-Flags.html.
129 // FIXME: Blocked. Possible race condition otherwise.
130 return ::sem_open(Nonnull(n),
131 int(omode), platform::DefaultPMode(), init);
132 });
133 });
134
135 auto p(create(OpenMode::CreateExclusive));
136
137 if(p != SEM_FAILED)
138 name = n;
139 else
140 {
141 // FIXME: Blocked. TOCTTOU access. The semaphore might be recreated
142 // before the call.
143 if(errno == EEXIST)
144 p = create(OpenMode::Create);
145 // FIXME: Blocked. TOCTTOU access. The semaphore might be recreated
146 // and the creation state is not recorded properly, missing
147 // %::sem_unlink would cause leak.
148 if(YB_UNLIKELY(p == SEM_FAILED))
149 YCL_Raise_SysE(, "::sem_open", yfsig);
150 }
151 return p;
152 }())
153 #endif
154 {
155 #if !YCL_Win32
156 h_sem.get_deleter().Referent = make_observer(this);
157 #endif
158 }
159 Semaphore::Semaphore(const char16_t* n, CountType init)
160 #if YCL_Win32
161 // TODO: Allow setting of security attributes.
162 : h_sem(YCL_CallF_Win32(CreateSemaphore, {}, init,
163 std::numeric_limits<CountType>::max(), platform::wcast(n)))
164 #else
165 : Semaphore(platform::MakePathString(n).c_str(), init)
166 #endif
167 {}
168
169 void
170 Semaphore::lock()
171 {
172 #if YCL_Win32
173 WaitUnique(native_handle());
174 #else
175 platform::RetryOnInterrupted(std::bind(YCL_WrapCall_CAPI(, ::sem_wait,
176 static_cast<::sem_t*>(native_handle())), yfsig));
177 #endif
178 }
179
180 bool
181 Semaphore::try_lock() ynothrow
182 {
183 #if YCL_Win32
184 return TryWaitUnique(native_handle());
185 #else
186 return platform::RetryOnInterrupted([this]{
187 return ::sem_trywait(static_cast<::sem_t*>(native_handle()));
188 }) == 0;
189 #endif
190 }
191
192 void
193 Semaphore::unlock() ynothrow
194 {
195 #if YCL_Win32
196 const auto
197 res(YCL_TraceCallF_Win32(ReleaseSemaphore, native_handle(), 1, {}));
198
199 YAssert(res, "Invalid semaphore found.");
200 #else
201 const auto res(YCL_TraceCallF_CAPI(::sem_post,
202 static_cast<::sem_t*>(native_handle())));
203
204 YAssert(res == 0, "Invalid semaphore found.");
205 #endif
206 yunused(res);
207 }
208
209
210 pair<UniqueHandle, UniqueHandle>
211 MakePipe()
212 {
213 # if YCL_Win32
214 ::HANDLE h_raw_read, h_raw_write;
215
216 YCL_CallF_Win32(CreatePipe, &h_raw_read, &h_raw_write, {}, 0);
217
218 UniqueHandle h_read(h_raw_read), h_write(h_raw_write);
219
220 YCL_CallF_Win32(SetHandleInformation, h_write.get(), HANDLE_FLAG_INHERIT,
221 HANDLE_FLAG_INHERIT);
222 return {std::move(h_read), std::move(h_write)};
223 # elif YCL_API_Has_unistd_h
224 int fds[2];
225
226 // TODO: Check whether '::socketpair' is available.
227 YCL_CallF_CAPI(, ::pipe, fds);
228
229 auto pr(make_pair(UniqueHandle(fds[0]), UniqueHandle(fds[1])));
230 auto check([](UniqueHandle& h, const char* msg) YB_NONNULL(3){
231 // NOTE: %O_NONBLOCK is initially cleared on ::pipe results.
232 // See http://pubs.opengroup.org/onlinepubs/9699919799/.
233 if(h)
234 h->SetNonblocking();
235 else
236 ystdex::throw_error(std::errc::bad_file_descriptor, msg);
237 });
238
239 check(pr.first, "Failed making pipe for reading."),
240 check(pr.second, "Failed making pipe for writing.");
241 return pr;
242 # else
243 # error "Unsupported platform found."
244 # endif
245 }
246
247
248 # if YCL_Win32
249 string
250 DecodeArg(const char* str)
251 {
252 return MBCSToMBCS(str, CP_ACP, CP_UTF8);
253 }
254
255 string
256 EncodeArg(const char* str)
257 {
258 return MBCSToMBCS(str);
259 }
260 # endif
261
262
264 namespace
265 {
266
267 template<typename _func, typename _tPointer, typename... _tParams>
268 bool
269 CallTe(_func pmf, _tPointer& p, _tParams&... args)
270 {
271 return ystdex::call_value_or(ystdex::bind1(pmf, std::ref(args)...), p);
272 }
273
274 } // unnamed namespace;
275
276 # if YCL_Win32
277 class TerminalData : private WConsole
278 {
279 public:
281 TerminalData(int fd)
282 : WConsole(ToHandle(fd))
283 {}
284
286 PDefH(bool, Clear, )
287 ImplRet(WConsole::Clear(), true)
288
289 PDefH(bool, RestoreAttributes, )
290 ImplRet(WConsole::RestoreAttributes(), true)
291
292 PDefH(bool, UpdateForeColor, std::uint8_t c)
293 ImplRet(WConsole::UpdateForeColor(c), true)
294 };
295 # else
296 namespace
298 {
299
300 yconstexpr const int cmap[] = {0, 4, 2, 6, 1, 5, 3, 7};
301
302 } //unnamed namespace
303
305 class TerminalData : private noncopyable, private nonmovable
306 {
307 private:
309 std::FILE* stream;
310
311 public:
312 TerminalData(std::FILE* fp)
313 : stream(fp)
314 {}
315
321 bool
322 ExecuteCachedCommand(string_view) const;
323
325 PDefH(bool, Clear, ) ynothrow
326 ImplRet(ExecuteCachedCommand("tput clear"))
327
328 PDefH(bool, RestoreAttributes, ) ynothrow
329 ImplRet(ExecuteCachedCommand("tput sgr0"))
330
331 PDefH(bool, UpdateForeColor, std::uint8_t c) ynothrow
332 ImplRet(ExecuteCachedCommand("tput setaf " + to_string(cmap[c & 7]))
333 && (c < ystdex::underlying(YSLib::Consoles::DarkGray)
334 || ExecuteCachedCommand("tput bold")))
335 };
336
337 bool
338 TerminalData::ExecuteCachedCommand(string_view cmd) const
339 {
340 YAssertNonnull(cmd.data());
341
342 if(!cmd.empty())
343 {
344 const auto& str(FetchCachedCommandResult(cmd));
345
346 if(!str.empty())
347 {
348 std::fprintf(Nonnull(stream), "%s", str.c_str());
349 return true;
350 }
351 }
352 return {};
353 }
354 # endif
355
356
357 Terminal::Guard::~Guard()
358 {
359 if(p_terminal && *p_terminal)
360 FilterExceptions([this]{
361 if(YB_UNLIKELY(!p_terminal->p_data->RestoreAttributes()))
362 throw LoggedEvent("Restoring terminal attributes failed.");
363 });
364 }
365
366
367 Terminal::Terminal(std::FILE* fp)
368 : p_data([fp]()->TerminalData*{
369 # if !YCL_Win32
370 // XXX: Performance?
371 ystdex::setnbuf(fp);
372 # endif
373 const int fd(YCL_CallGlobal(fileno, Nonnull(fp)));
374
375 // NOTE: This is not necessary for Windows since it only determine
376 // whether the file descriptor is associated with a character device.
377 // However as a optimization, it is somewhat more efficient for some
378 // cases. See $2015-01 @ %Documentation::Workflow.
379 if(YCL_CallGlobal(isatty, fd))
380 # if YCL_Win32
381 TryRet(new TerminalData(fd))
382 # else
383 TryRet(new TerminalData(fp))
384 # endif
385 CatchExpr(Exception& e,
386 YTraceDe(Informative, "Creating console failed: %s.", e.what()))
387 return {};
388 }())
389 {}
390
391 ImplDeDtor(Terminal)
392
393 bool
394 Terminal::Clear()
395 {
396 return CallTe(&TerminalData::Clear, p_data);
397 }
398
399 Terminal::Guard
400 Terminal::LockForeColor(std::uint8_t c)
401 {
402 return Guard(*this, &Terminal::UpdateForeColor, c);
403 }
404
405 bool
406 Terminal::RestoreAttributes()
407 {
408 return CallTe(&TerminalData::RestoreAttributes, p_data);
409 }
410
411 bool
412 Terminal::UpdateForeColor(std::uint8_t c)
413 {
414 return CallTe(&TerminalData::UpdateForeColor, p_data, c);
415 }
416
417 bool
418 UpdateForeColorByLevel(Terminal& term, RecordLevel lv)
419 {
420 if(term)
421 {
422 using namespace Consoles;
423 static yconstexpr const RecordLevel
424 lvs[]{Err, Warning, Notice, Informative, Debug};
425 static yconstexpr const Color
426 colors[]{Red, Yellow, Cyan, Magenta, DarkGreen};
427 const auto
428 i(std::lower_bound(ystdex::begin(lvs), ystdex::end(lvs), lv));
429
430 if(i == ystdex::end(lvs))
431 term.RestoreAttributes();
432 else
433 term.UpdateForeColor(colors[i - lvs]);
434 return true;
435 }
436 return {};
437 }
438
439 } // namespace platform_ex;
440
441 #endif
442
ystdex::single_thread::try_lock
int try_lock(_tLock1 &&, _tLock2 &&, _tLocks &&...)
Definition: pseudo_mutex.h:983
YCL_CallGlobal
#define YCL_CallGlobal(_n,...)
调用按实现修饰的具有全局保留名称的函数。
Definition: NativeAPI.h:67
NPL::A1::Guard
any Guard
作用域守卫类型。
Definition: NPLA1.h:269
platform::Nonnull
YB_PURE _type && Nonnull(_type &&p) ynothrowv
断言并返回非空参数。
Definition: Debug.h:518
YSLib::UI::ynothrow
AView &y ynothrow
Definition: YWidgetView.cpp:47
CHRLib::c
c
Definition: CharacterProcessing.h:84
yconstexpr
#define yconstexpr
指定编译时常量表达式。
Definition: ydef.h:1072
ystdex::bind1
auto bind1(_func &&f, _tParams &&... args) -> decltype(std::bind(std::forward< decltype(f)>(f), std::placeholders::_1, std::forward< decltype(args)>(args)...))
复合调用 std::bind 和 std::placeholders::_1 。
Definition: functional.hpp:53
platform::RetryOnInterrupted
_type RetryOnInterrupted(_func f)
循环可能被中断的操作。
Definition: YCommon.h:373
platform_ex
平台扩展命名空间。
Definition: Android.h:46
ystdex::retry_on_cond
invoke_result_t< _fCallable, _tParams... > retry_on_cond(_fCond cond, _fCallable &&f, _tParams &&... args)
循环重复调用：代替直接使用 do-while 语句以避免过多引入作用域外的变量。
Definition: functional.hpp:554
NPL::A1::Internals::term
_fCurrent TermNode &decltype(cur(term, ctx)) term
Definition: NPLA1Internals.h:593
yfsig
#define yfsig
展开为表示函数签名的预定义变量的宏。
Definition: ydef.h:1133
platform::Descriptions::RecordLevel
RecordLevel
记录等级。
Definition: YCommon.h:178
ystdex::cpp2017::string_view
basic_string_view< char > string_view
Definition: string_view.hpp:437
YSLib::UI::PDefH
PDefH(void, DisplayFor, IWidget &wgt, Timers::TimeSpan delay) ImplExpr(Show(wgt)
在一定时间内显示部件。
YSLib::FetchCachedCommandResult
YF_API const string & FetchCachedCommandResult(string_view, size_t=DefaultCommandBufferSize)
取缓冲的命令执行结果。
Definition: YCoreUtilities.cpp:123
platform::MakePathString
YB_ATTR_nodiscard MakePathString
Definition: FileIO.h:62
YSLib::Consoles::Red
@ Red
Definition: YConsole.h:65
ystdex::cpp2014::max
const _type & max(const _type &a, const _type &b, _fComp comp=less< _type >())
取较大的元素。
Definition: algorithm.hpp:635
YSLib::Consoles::Cyan
@ Cyan
Definition: YConsole.h:64
platform::strings::to_std_string
_tString to_std_string(basic_string_view< _tChar > sv)
Definition: Container.h:256
YSLib::UI::ImplDeDtor
ImplDeDtor(Thumb) void DecorateAsCloseButton(Thumb &tmb)
Definition: Button.cpp:125
YSLib::lv
const char RecordLevel lv
Definition: YException.cpp:61
YSLib::UI::args
yconstfn const string _tParams && args
Definition: Loader.h:131
ystdex::bases::nonmovable
不可转移对象：禁止继承此类的对象调用默认原型的转移构造函数和转移赋值操作符。
Definition: base.h:90
YCL_Raise_SysE
#define YCL_Raise_SysE(_t, _msg, _sig)
按错误值和指定参数抛出第一参数指定类型的对象。
Definition: YCommon.h:201
YSLib::YAssert
YAssert(!(v< Deref(a)), "Value less than lower bound found.")
platform_ex::Windows::MBCSToMBCS
YF_API string MBCSToMBCS(string_view, unsigned=CP_UTF8, unsigned=CP_ACP)
YSLib::LoggedEvent
记录日志的异常事件类。
Definition: YException.h:51
ystdex::make_observer
observer_ptr< _type > make_observer(_type *p)
Definition: pointer.hpp:405
ystdex::setnbuf
bool setnbuf(std::FILE *stream)
关闭流缓冲。
Definition: ystdex/cstdio.h:62
platform_ex::Windows::WaitUnique
YF_API void WaitUnique(UniqueHandle::pointer, unsigned long=INFINITE)
以指定的时间间隔上限等待句柄指定的同步对象。
ystdex::cpp2017::data
auto data(_tRange &c) -> decltype(c.data())
Definition: range.hpp:581
platform::Descriptions::Notice
@ Notice
Definition: YCommon.h:184
ystdex::cpp2017::end
auto end(_tRange &c) -> decltype(c.end())
Definition: range.hpp:345
platform_ex::Windows::WConsole
Windows 控制台对象。
Definition: Consoles.h:70
platform::Descriptions::Informative
@ Informative
Definition: YCommon.h:185
YSLib::Consoles::Yellow
@ Yellow
Definition: YConsole.h:67
YSLib::UI::name
yconstfn const string & name
Definition: Loader.h:130
std
Definition: optional.h:982
YSLib::Consoles::DarkGreen
@ DarkGreen
Definition: YConsole.h:55
YAssertNonnull
#define YAssertNonnull(_expr)
Definition: cassert.h:165
YSLib::i
return i
Definition: YCoreUtilities.h:177
ystdex::throw_error
void throw_error(std::error_code ec, _tParams &&... args)
抛出错误： std::system_error 或允许相同构造函数参数的异常。
Definition: ystdex/exception.h:241
YTraceDe
#define YTraceDe(...)
YCLib 默认调试跟踪。
Definition: Debug.h:407
YSLib::FilterExceptions
bool FilterExceptions(_func f, const char *desc={}, RecordLevel lv=Alert, ExceptionTracer trace=ExtractAndTrace) ynothrow
调用函数并过滤宿主异常。
Definition: YException.h:225
platform::Descriptions::Err
@ Err
Definition: YCommon.h:182
YCL_CallF_Win32
#define YCL_CallF_Win32(_fn,...)
Definition: Win32/include/YCLib/MinGW32.h:199
ystdex::to_string
std::string to_string(const std::thread::id &)
转换 thread::id 为字符串表示。
platform::Descriptions::Warning
@ Warning
Definition: YCommon.h:183
YSLib::UI::e
void e
Definition: YWidget.h:356
CatchExpr
#define CatchExpr(_ex,...)
指定表达式作为异常处理器。
Definition: YBaseMacro.h:129
YCL_WrapCall_CAPI
#define YCL_WrapCall_CAPI(_t, _fn,...)
调用系统 C API 或其它可用 errno 取得调用状态的例程。
Definition: YCommon.h:248
platform
默认平台命名空间。
Definition: Container.h:56
YSLib
YSLib 命名空间。
Definition: AndroidHost.h:46
Color
Pixel Color
Definition: CharRenderer.cpp:59
YSLib::str
const char * str
Definition: YException.cpp:60
YCL_TraceCallF_Win32
#define YCL_TraceCallF_Win32(_fn,...)
Definition: Win32/include/YCLib/MinGW32.h:220
YCL_TraceCallF_CAPI
#define YCL_TraceCallF_CAPI(_fn,...)
Definition: YCommon.h:281
YSLib::Consoles::Magenta
@ Magenta
Definition: YConsole.h:66
CHRLib::char16_t
YB_ATTR_nodiscard YB_PURE char16_t((*c_ptr<< CHAR_BIT)|c_ptr[1])) YB_ATTR_nodiscard YB_PURE inline PDefH(char16_t
取 c_ptr 指向的小端序双字节字符。
ynothrowv
#define ynothrowv
YSLib 无异常抛出保证验证：有条件地使用无异常抛出规范。
Definition: ydef.h:1219
ystdex
<memory> 特性测试宏。
Definition: ydef.h:1262
YSLib::_tParams
YB_ATTR_nodiscard YB_PURE typename _tParams
Definition: ValueNode.h:1294
YSLib::YB_NONNULL
YB_ATTR_nodiscard YF_API YB_NONNULL(1, 2) ValueNode LoadNPLA1File(const char *disp
载入 NPLA1 配置文件。
YModules.h
YCLib 库模块配置文件。
NPL::c_str
YF_API const char src c_str())) YF_API void InstallDirectory(const string &
安装目录：复制目录树。
Definition: Dependency.h:140
fp
tidy_ptr< std::streambuf > fp
Definition: TextManager.cpp:93
YSLib::Consoles::DarkGray
@ DarkGray
Definition: YConsole.h:61
ystdex::underlying
enable_if_t< is_enum_union< _type >::value, wrapped_enum_traits_t< _type > > underlying(_type val)
Definition: enum.hpp:112
YCL_CallF_CAPI
#define YCL_CallF_CAPI(_t, _fn,...)
Definition: YCommon.h:260
YB_UNLIKELY
#define YB_UNLIKELY(_expr)
分支预测提示。
Definition: ydef.h:828
platform::p
return p p
Definition: YCLib/FileSystem.h:209
ystdex::single_thread::lock
void lock(_tLock1 &&, _tLock2 &&, _tLocks &&...)
Definition: pseudo_mutex.h:990
ystdex::cpp2017::string
basic_string< char > string
Definition: string.hpp:772
ystdex::bases::noncopyable
不可复制对象：禁止派生类调用默认原型的复制构造函数和复制赋值操作符。
Definition: base.h:50
TryRet
#define TryRet(...)
指定表达式在 try 块中求值并返回。
Definition: YBaseMacro.h:124
yunused
#define yunused(...)
标记未使用的表达式。
Definition: ydef.h:1458
platform_ex::Windows::TryWaitUnique
YF_API bool TryWaitUnique(UniqueHandle::pointer, unsigned long=0) ynothrow
ystdex::ref
void ref(const _type &&)=delete
YSLib::UI::ImplRet
const IWidget &wgt ImplRet(wgt.GetView().IsVisible()) YB_ATTR_nodiscard YB_PURE bool YF_API Contains(const IWidget &
判断点是否在部件的可视区域内。
ystdex::call_value_or
auto call_value_or(_func f, _type &&p) -> decay_t< decltype(f(*std::forward< decltype(p)>(p)))>
调用非空值或取默认值。
Definition: deref_op.hpp:152
ystdex::cpp2017::begin
auto begin(_tRange &c) -> decltype(c.begin())
Definition: range.hpp:323
YSLib::level
YF_API size_t level
Definition: YException.h:156
platform::Descriptions::Debug
@ Debug
Definition: YCommon.h:186
platform::DefaultPMode
YB_ATTR_nodiscard YF_API YB_STATELESS mode_t DefaultPMode() ynothrow
取默认权限。
Definition: FileIO.cpp:770