#pragma once #include #include #include #include #include #include #include #include #include #if !defined(_WIN32) #include #else // defined(_WIN32) #include #include #endif // defined(_WIN32) namespace c10 { namespace detail { // Creates the filename pattern passed to and completed by `mkstemp`. // Returns std::vector because `mkstemp` needs a (non-const) `char*` and // `std::string` only provides `const char*` before C++17. #if !defined(_WIN32) inline std::vector make_filename(std::string name_prefix) { // The filename argument to `mkstemp` needs "XXXXXX" at the end according to // http://pubs.opengroup.org/onlinepubs/009695399/functions/mkstemp.html static const std::string kRandomPattern = "XXXXXX"; // We see if any of these environment variables is set and use their value, or // else default the temporary directory to `/tmp`. static const char* env_variables[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; std::string tmp_directory = "/tmp"; for (const char* variable : env_variables) { if (const char* path = getenv(variable)) { tmp_directory = path; break; } } std::vector filename; filename.reserve( tmp_directory.size() + name_prefix.size() + kRandomPattern.size() + 2); filename.insert(filename.end(), tmp_directory.begin(), tmp_directory.end()); filename.push_back('/'); filename.insert(filename.end(), name_prefix.begin(), name_prefix.end()); filename.insert(filename.end(), kRandomPattern.begin(), kRandomPattern.end()); filename.push_back('\0'); return filename; } #endif // !defined(_WIN32) } // namespace detail struct TempFile { #if !defined(_WIN32) TempFile() : fd(-1) {} TempFile(std::string name, int fd) : fd(fd), name(std::move(name)) {} TempFile(const TempFile&) = delete; TempFile(TempFile&& other) noexcept : fd(other.fd), name(std::move(other.name)) { other.fd = -1; other.name.clear(); } TempFile& operator=(const TempFile&) = delete; TempFile& operator=(TempFile&& other) { fd = other.fd; name = std::move(other.name); other.fd = -1; other.name.clear(); return *this; } ~TempFile() { if (fd >= 0) { unlink(name.c_str()); close(fd); } } int fd; #endif // !defined(_WIN32) std::string name; }; struct TempDir { TempDir() = default; explicit TempDir(std::string name) : name(std::move(name)) {} TempDir(const TempDir&) = delete; TempDir(TempDir&& other) noexcept : name(std::move(other.name)) { other.name.clear(); } TempDir& operator=(const TempDir&) = delete; TempDir& operator=(TempDir&& other) { name = std::move(other.name); other.name.clear(); return *this; } ~TempDir() { if (!name.empty()) { #if !defined(_WIN32) rmdir(name.c_str()); #else // defined(_WIN32) RemoveDirectoryA(name.c_str()); #endif // defined(_WIN32) } } std::string name; }; /// Attempts to return a temporary file or returns `nullopt` if an error /// occurred. /// /// The file returned follows the pattern /// `/`, where `` is the value of /// the `"TMPDIR"`, `"TMP"`, `"TEMP"` or /// `"TEMPDIR"` environment variable if any is set, or otherwise `/tmp`; /// `` is the value supplied to this function, and /// `` is a random sequence of numbers. /// On Windows, `name_prefix` is ignored and `tmpnam` is used. inline c10::optional try_make_tempfile( std::string name_prefix = "torch-file-") { #if defined(_WIN32) return TempFile{std::tmpnam(nullptr)}; #else std::vector filename = detail::make_filename(std::move(name_prefix)); const int fd = mkstemp(filename.data()); if (fd == -1) { return c10::nullopt; } // Don't make the string from string(filename.begin(), filename.end(), or // there will be a trailing '\0' at the end. return TempFile(filename.data(), fd); #endif // defined(_WIN32) } /// Like `try_make_tempfile`, but throws an exception if a temporary file could /// not be returned. inline TempFile make_tempfile(std::string name_prefix = "torch-file-") { if (auto tempfile = try_make_tempfile(std::move(name_prefix))) { return std::move(*tempfile); } TORCH_CHECK(false, "Error generating temporary file: ", std::strerror(errno)); } /// Attempts to return a temporary directory or returns `nullopt` if an error /// occurred. /// /// The directory returned follows the pattern /// `//`, where `` is the value /// of the `"TMPDIR"`, `"TMP"`, `"TEMP"` or /// `"TEMPDIR"` environment variable if any is set, or otherwise `/tmp`; /// `` is the value supplied to this function, and /// `` is a random sequence of numbers. /// On Windows, `name_prefix` is ignored and `tmpnam` is used. inline c10::optional try_make_tempdir( std::string name_prefix = "torch-dir-") { #if defined(_WIN32) while (true) { const char* dirname = std::tmpnam(nullptr); if (!dirname) { return c10::nullopt; } if (CreateDirectoryA(dirname, NULL)) { return TempDir(dirname); } if (GetLastError() != ERROR_ALREADY_EXISTS) { return c10::nullopt; } } return c10::nullopt; #else std::vector filename = detail::make_filename(std::move(name_prefix)); const char* dirname = mkdtemp(filename.data()); if (!dirname) { return c10::nullopt; } return TempDir(dirname); #endif // defined(_WIN32) } /// Like `try_make_tempdir`, but throws an exception if a temporary directory /// could not be returned. inline TempDir make_tempdir(std::string name_prefix = "torch-dir-") { if (auto tempdir = try_make_tempdir(std::move(name_prefix))) { return std::move(*tempdir); } TORCH_CHECK( false, "Error generating temporary directory: ", std::strerror(errno)); } } // namespace c10