diff options
Diffstat (limited to 'source/core')
| -rw-r--r-- | source/core/slang-io.cpp | 164 | ||||
| -rw-r--r-- | source/core/slang-io.h | 73 |
2 files changed, 207 insertions, 30 deletions
diff --git a/source/core/slang-io.cpp b/source/core/slang-io.cpp index 345a2c353..d8ef48ee3 100644 --- a/source/core/slang-io.cpp +++ b/source/core/slang-io.cpp @@ -29,6 +29,7 @@ # include <dirent.h> # include <sys/stat.h> +# include <sys/file.h> #endif #if SLANG_APPLE_FAMILY @@ -272,7 +273,7 @@ namespace Slang else return ""; } - + /* static */void Path::append(StringBuilder& ioBuilder, const UnownedStringSlice& path) { if (ioBuilder.getLength() == 0) @@ -287,7 +288,7 @@ namespace Slang { ioBuilder.append(kPathDelimiter); } - // Check that path doesn't start with a path delimiter + // Check that path doesn't start with a path delimiter SLANG_ASSERT(!isDelimiter(path[0])); // Append the path ioBuilder.append(path); @@ -325,7 +326,7 @@ namespace Slang { switch (element.getLength()) { - case 0: + case 0: { // We'll just assume it is return true; @@ -333,7 +334,7 @@ namespace Slang case 2: { // Look for a windows like drive spec - const char firstChar = element[0]; + const char firstChar = element[0]; return element[1] == ':' && ((firstChar >= 'a' && firstChar <= 'z') || (firstChar >= 'A' && firstChar <= 'Z')); } default: return false; @@ -363,7 +364,7 @@ namespace Slang return true; } - // Check for drive + // Check for drive if (isDriveSpecification(getFirstElement(path))) { return true; @@ -387,19 +388,19 @@ namespace Slang while (cur < end && !isDelimiter(*cur)) cur++; splitOut.add(UnownedStringSlice(start, cur)); - + // Next start = cur + 1; } - // Okay if the end is empty. And we aren't with a spec like // or c:/ , then drop the final slash + // Okay if the end is empty. And we aren't with a spec like // or c:/ , then drop the final slash if (splitOut.getCount() > 1 && splitOut.getLast().getLength() == 0) { if (splitOut.getCount() == 2 && isDriveSpecification(splitOut[0])) { return; } - // Remove the last + // Remove the last splitOut.removeLast(); } } @@ -440,7 +441,7 @@ namespace Slang return SLANG_E_NOT_FOUND; } - // We allow splitPath.getCount() == 0, because + // We allow splitPath.getCount() == 0, because // the original path could have been '.' or './.' // // Special handling this case is in Path::join @@ -470,7 +471,7 @@ namespace Slang const UnownedStringSlice& cur = ioSplit[i]; if (cur == "." && ioSplit.getCount() > 1) { - // Just remove it + // Just remove it ioSplit.removeAt(i); i--; } @@ -525,7 +526,7 @@ namespace Slang { #if defined(_WIN32) return _wmkdir(path.toWString()) == 0; -#else +#else return mkdir(path.getBuffer(), 0777) == 0; #endif } @@ -581,14 +582,14 @@ namespace Slang if (!absPath) { return SLANG_FAIL; - } + } canonicalPathOut = String::fromWString(absPath); ::free(absPath); return SLANG_OK; #else # if 1 - + // http://man7.org/linux/man-pages/man3/realpath.3.html char* canonicalPath = ::realpath(path.begin(), nullptr); if (canonicalPath) @@ -637,7 +638,7 @@ namespace Slang SlangPathType pathType; SLANG_RETURN_ON_FAIL(getPathType(path, &pathType)); - + switch (pathType) { case SLANG_PATH_TYPE_FILE: @@ -706,7 +707,7 @@ namespace Slang /* static */SlangResult Path::find(const String& directoryPath, const char* pattern, Visitor* visitor) { DIR* directory = opendir(directoryPath.getBuffer()); - + if (!directory) { return SLANG_E_NOT_FOUND; @@ -766,7 +767,7 @@ namespace Slang /// Gets the path to the executable that was invoked that led to the current threads execution /// If run from a shared library/dll will be the path of the executable that loaded said library /// @param outPath Pointer to buffer to hold the path. - /// @param ioPathSize Size of the buffer to hold the path (including zero terminator). + /// @param ioPathSize Size of the buffer to hold the path (including zero terminator). /// @return SLANG_OK on success, SLANG_E_BUFFER_TOO_SMALL if buffer is too small. If ioPathSize is changed it will be the required size static SlangResult _calcExectuablePath(char* outPath, size_t* ioSize) { @@ -776,7 +777,7 @@ namespace Slang #if SLANG_WINDOWS_FAMILY // https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-getmodulefilenamea - + DWORD res = ::GetModuleFileNameA(::GetModuleHandle(nullptr), outPath, DWORD(bufferSize)); // If it fits it's the size not including terminator. So must be less than bufferSize if (res < bufferSize) @@ -801,7 +802,7 @@ namespace Slang // Zero terminate outPath[resSize - 1] = 0; return SLANG_OK; -# else +# else String text = Slang::File::readAllText("/proc/self/maps"); Index startIndex = text.indexOf('/'); if (startIndex == Index(-1)) @@ -963,7 +964,7 @@ namespace Slang SLANG_RETURN_ON_FAIL(stream.write(data, size)); return SLANG_OK; } - + SlangResult File::writeAllText(const Slang::String& fileName, const Slang::String& text) { RefPtr<FileStream> stream = new FileStream; @@ -975,7 +976,7 @@ namespace Slang return SLANG_OK; } - + /* static */SlangResult File::writeNativeText(const String& path, const void* data, size_t size) { FILE* file = fopen(path.getBuffer(), "w"); @@ -1075,4 +1076,127 @@ namespace Slang return uri; } + + SlangResult LockFile::open(const String& fileName) + { +#if SLANG_WINDOWS_FAMILY + m_fileHandle = ::CreateFileW( + fileName.toWString(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL + ); + m_isOpen = m_fileHandle != INVALID_HANDLE_VALUE; +#else + m_fileHandle = ::open(fileName.getBuffer(), O_RDWR | O_CREAT, 0600); + m_isOpen = m_fileHandle != -1; +#endif + return m_isOpen ? SLANG_OK : SLANG_E_CANNOT_OPEN; + } + + void LockFile::close() + { + if (!m_isOpen) + return; + +#if SLANG_WINDOWS_FAMILY + ::CloseHandle(m_fileHandle); +#else + ::close(m_fileHandle); +#endif + + m_isOpen = false; + } + + SlangResult LockFile::tryLock(LockType lockType) + { + if (!m_isOpen) + return SLANG_E_CANNOT_OPEN; + + SlangResult result = SLANG_OK; +#if SLANG_WINDOWS_FAMILY + OVERLAPPED overlapped = {0}; + DWORD flags = lockType == LockType::Shared ? LOCKFILE_FAIL_IMMEDIATELY : (LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY); + if (::LockFileEx(m_fileHandle, flags, DWORD(0), ~DWORD(0), ~DWORD(0), &overlapped) == 0) + { + result = SLANG_E_TIME_OUT; + } +#else + int operation = lockType == LockType::Shared ? (LOCK_SH | LOCK_NB) : (LOCK_EX | LOCK_NB); + if (::flock(m_fileHandle, operation) != 0) + { + result = SLANG_E_TIME_OUT; + } +#endif + return result; + } + + SlangResult LockFile::lock(LockType lockType) + { + if (!m_isOpen) + return SLANG_E_CANNOT_OPEN; + + SlangResult result = SLANG_OK; +#if SLANG_WINDOWS_FAMILY + OVERLAPPED overlapped = {0}; + overlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); + DWORD flags = lockType == LockType::Shared ? 0 : LOCKFILE_EXCLUSIVE_LOCK; + if (::LockFileEx(m_fileHandle, flags, DWORD(0), ~DWORD(0), ~DWORD(0), &overlapped) == 0) + { + auto err = ::GetLastError(); + if (err == ERROR_IO_PENDING) + { + DWORD bytes; + if (::GetOverlappedResult(m_fileHandle, &overlapped, &bytes, TRUE) == 0) + { + result = SLANG_E_INTERNAL_FAIL; + } + } + else + { + result = SLANG_E_INTERNAL_FAIL; + } + } + ::CloseHandle(overlapped.hEvent); +#else + int operation = lockType == LockType::Shared ? LOCK_SH : LOCK_EX; + if (::flock(m_fileHandle, operation) != 0) + { + result = SLANG_E_INTERNAL_FAIL; + } +#endif + return result; +} + + SlangResult LockFile::unlock() + { + if (!m_isOpen) + return SLANG_E_CANNOT_OPEN; + +#if SLANG_WINDOWS_FAMILY + OVERLAPPED overlapped = {0}; + if (::UnlockFileEx(m_fileHandle, DWORD(0), ~DWORD(0), ~DWORD(0), &overlapped) == 0) + { + return SLANG_E_INTERNAL_FAIL; + } +#else + if (::flock(m_fileHandle, LOCK_UN) != 0) + { + return SLANG_E_INTERNAL_FAIL; + } +#endif + return SLANG_OK; +} + + LockFile::LockFile() + : m_isOpen(false) + {} + + LockFile::~LockFile() + { + close(); + } } diff --git a/source/core/slang-io.h b/source/core/slang-io.h index 292328603..fc5cbfa9d 100644 --- a/source/core/slang-io.h +++ b/source/core/slang-io.h @@ -25,14 +25,14 @@ namespace Slang static SlangResult writeNativeText(const String& filename, const void* data, size_t size); static SlangResult writeAllBytes(const String& fileName, const void* data, size_t size); - + static SlangResult remove(const String& fileName); static SlangResult makeExecutable(const String& fileName); /// Creates a temporary file typically in some way based on the prefix /// The file will be *created* with the outFileName, on success. - /// It's creation in necessary to lock that particular name. + /// It's creation in necessary to lock that particular name. static SlangResult generateTemporary(const UnownedStringSlice& prefix, String& outFileName); }; @@ -47,15 +47,15 @@ namespace Slang { /// Can only simplify to an absolute path. Will return an error if not possible. /// Useful to constrain a path, such as when wanting something like 'chroot'. - AbsoluteOnly = 0x1, - /// If the simplified path is a root path, remove the root. + AbsoluteOnly = 0x1, + /// If the simplified path is a root path, remove the root. /// Will mean that for example /// "/" -> "." /// "/a/.." -> "." /// "/a" -> "a" - /// Its worth noting that a path prefixed "/" will never be returned and if *just* the root it specified + /// Its worth noting that a path prefixed "/" will never be returned and if *just* the root it specified /// it will return as ".". - NoRoot = 0x2, + NoRoot = 0x2, }; }; @@ -136,7 +136,7 @@ namespace Slang /// Combine path sections and store the result in outBuilder static void combineIntoBuilder(const UnownedStringSlice& path1, const UnownedStringSlice& path2, StringBuilder& outBuilder); - /// Append a path, taking into account path separators onto the end of ioBuilder + /// Append a path, taking into account path separators onto the end of ioBuilder static void append(StringBuilder& ioBuilder, const UnownedStringSlice& path); static bool createDirectory(const String& path); @@ -155,12 +155,12 @@ namespace Slang /// Relative paths that are in effect "." will become [] static void split(const UnownedStringSlice& path, List<UnownedStringSlice>& splitOut); - /// Strips .. and . as much as it can + /// Strips .. and . as much as it can static String simplify(const UnownedStringSlice& path); static String simplify(const String& path) { return simplify(path.getUnownedSlice()); } /// Given a path simplifies it such the the resultant path is absolute (ie contains no . or ..) - /// Same behavior as simplify around the root + /// Same behavior as simplify around the root static SlangResult simplify(const UnownedStringSlice& path, SimplifyStyle style, StringBuilder& outPath); static SlangResult simplify(const String& path, SimplifyStyle style, StringBuilder& outPath) { return simplify(path.getUnownedSlice(), style, outPath); } static SlangResult simplify(const char* path, SimplifyStyle style, StringBuilder& outPath) { return simplify(UnownedStringSlice(path), style, outPath); } @@ -199,7 +199,7 @@ namespace Slang /// Returns the first element of the path or an empty slice if there is none /// This broadly equivalent to returning the first element of split /// @param path Path to extract first element from - /// @return The first element of the path, or empty + /// @return The first element of the path, or empty static UnownedStringSlice getFirstElement(const UnownedStringSlice& path); /// Remove a file or directory at specified path. The directory must be empty for it to be removed @@ -225,6 +225,59 @@ namespace Slang static bool isSafeURIChar(char ch); }; + /// Helper class abstracting lock files. + /// Uses LockFileEx() on windows systems and flock() on POSIX systems. + class LockFile + { + public: + enum class LockType + { + Exclusive, + Shared, + }; + + /// Open the lock file. This will create the file if it doesn't exist yet. + /// @param fileName File name to open. + /// @return SLANG_OK on success. + SlangResult open(const String& fileName); + + /// Closes the lock file. + void close(); + + /// Returns true if the lock file is open. + bool isOpen() const { return m_isOpen; } + + /// Acquire the lock in non-blocking mode. + /// @param lockType Lock type (Exclusive or Shared). + /// @return SLANG_OK on success. SLANG_E_TIME_OUT if the lock is already held. + SlangResult tryLock(LockType lockType = LockType::Exclusive); + + /// Acquire the lock in blocking mode. + /// @param lockType Lock type (Exclusive or Shared). + /// @return SLANG_OK on success. + SlangResult lock(LockType lockType = LockType::Exclusive); + + /// Release the lock. + /// @return SLANG_OK on success. + SlangResult unlock(); + + LockFile(); + ~LockFile(); + + private: + LockFile(const LockFile&) = delete; + LockFile(LockFile&) = delete; + LockFile& operator=(const LockFile&) = delete; + LockFile& operator=(const LockFile&&) = delete; + +#if SLANG_WINDOWS_FAMILY + void* m_fileHandle; +#else + int m_fileHandle; +#endif + bool m_isOpen; + }; + } #endif |
