diff --git a/test/scan-test.cc b/test/scan-test.cc index 5d6585ef..e8fd75e5 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -144,4 +144,33 @@ TEST(scan_test, file) { EXPECT_EQ(n1, 10); EXPECT_EQ(n2, 20); } + +TEST(scan_test, lock) { + fmt::file read_end, write_end; + fmt::file::pipe(read_end, write_end); + + std::thread producer([&]() { + fmt::string_view input = "42 "; + for (int i = 0; i < 1000; ++i) + write_end.write(input.data(), input.size()); + write_end.close(); + }); + + fmt::buffered_file f = read_end.fdopen("r"); + auto fun = [&]() { + int value = 0; + while (fmt::scan(f.get(), "{}", value)) { + if (value != 42) { + read_end.close(); + EXPECT_EQ(value, 42); + break; + } + } + }; + std::thread consumer1(fun); + std::thread consumer2(fun); + producer.join(); + consumer1.join(); + consumer2.join(); +} #endif // FMT_USE_FCNTL diff --git a/test/scan.h b/test/scan.h index 3032b7bb..ea7c51ba 100644 --- a/test/scan.h +++ b/test/scan.h @@ -266,9 +266,20 @@ class file_scan_buffer : public scan_buffer { public: explicit file_scan_buffer(FILE* f) : scan_buffer(nullptr, nullptr, false), file_(f) { - // TODO: lock file? +#ifndef _WIN32 + flockfile(f); +#else + _lock_file(f); +#endif fill(); } + ~file_scan_buffer() { +#ifndef _WIN32 + funlockfile(file_); +#else + _unlock_file(file_); +#endif + } }; } // namespace detail @@ -472,7 +483,6 @@ struct scan_handler : error_handler { switch (arg_.type) { case scan_type::int_type: if (auto value = read_int()) *arg_.int_value = *value; - // TODO: stop on end of input break; case scan_type::uint_type: if (auto value = read_uint()) *arg_.uint_value = *value; @@ -512,6 +522,11 @@ struct scan_handler : error_handler { arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_); return parse_ctx_.begin(); } + + void on_error(const char* message) { + scan_ctx_.advance_to(scan_ctx_.end()); + error_handler::on_error(message); + } }; } // namespace detail @@ -533,9 +548,10 @@ auto scan(string_view input, string_view fmt, T&... args) return input.begin() + (buf.begin().base() - input.data()); } -template void scan(std::FILE* f, string_view fmt, T&... args) { +template bool scan(std::FILE* f, string_view fmt, T&... args) { auto&& buf = detail::file_scan_buffer(f); vscan(buf, fmt, make_scan_args(args...)); + return buf.begin() != buf.end(); } FMT_END_NAMESPACE