Starts an asynchronous file system operation (open, close, read, write, sendFile, stat, lstat, fstat, etc.) Some operations need a file path and others need a file descriptor. More...
#include <Async.h>
Public Types | |
enum class | Operation { None = 0 , Open , Close , Read , Write , CopyFile , CopyDirectory , Rename , RemoveDirectory , RemoveFile } |
using | CompletionData = AsyncFileSystemOperationCompletionData |
using | Result = AsyncResultOf<AsyncFileSystemOperation, CompletionData> |
![]() | |
enum class | Type : uint8_t { LoopTimeout , LoopWakeUp , LoopWork , ProcessExit , SocketAccept , SocketConnect , SocketSend , SocketSendTo , SocketReceive , SocketReceiveFrom , FileRead , FileWrite , FilePoll , FileSystemOperation } |
Type of async request. More... | |
Public Member Functions | |
SC::Result | setThreadPool (ThreadPool &threadPool) |
Sets the thread pool to use for the operation. | |
SC::Result | open (AsyncEventLoop &eventLoop, StringSpan path, FileOpen mode) |
Opens a file asynchronously and returns its corresponding file descriptor. | |
SC::Result | close (AsyncEventLoop &eventLoop, FileDescriptor::Handle handle) |
Closes a file descriptor asynchronously. | |
SC::Result | read (AsyncEventLoop &eventLoop, FileDescriptor::Handle handle, Span< char > buffer, uint64_t offset) |
Reads data from a file descriptor at a given offset. | |
SC::Result | write (AsyncEventLoop &eventLoop, FileDescriptor::Handle handle, Span< const char > buffer, uint64_t offset) |
Writes data to a file descriptor at a given offset. | |
SC::Result | copyFile (AsyncEventLoop &eventLoop, StringSpan path, StringSpan destinationPath, FileSystemCopyFlags copyFlags=FileSystemCopyFlags()) |
Copies a file from one location to another. | |
SC::Result | copyDirectory (AsyncEventLoop &eventLoop, StringSpan path, StringSpan destinationPath, FileSystemCopyFlags copyFlags=FileSystemCopyFlags()) |
Copies a directory from one location to another. | |
SC::Result | rename (AsyncEventLoop &eventLoop, StringSpan path, StringSpan newPath) |
Renames a file. | |
SC::Result | removeEmptyDirectory (AsyncEventLoop &eventLoop, StringSpan path) |
Removes a directory asynchronously. | |
SC::Result | removeFile (AsyncEventLoop &eventLoop, StringSpan path) |
Removes a file asynchronously. | |
![]() | |
void | setDebugName (const char *newDebugName) |
void | executeOn (AsyncSequence &sequence) |
Adds the request to be executed on a specific AsyncSequence. | |
Result | executeOn (AsyncTaskSequence &task, ThreadPool &pool) |
Adds the request to be executed on a specific AsyncTaskSequence. | |
void | disableThreadPool () |
Disables the thread-pool usage for this request. | |
AsyncRequest (Type type) | |
Constructs a free async request of given type. | |
Result | stop (AsyncEventLoop &eventLoop, Function< void(AsyncResult &)> *afterStopped=nullptr) |
Ask to stop current async operation. | |
bool | isFree () const |
Returns true if this request is free. | |
bool | isCancelling () const |
Returns true if this request is being cancelled. | |
bool | isActive () const |
Returns true if this request is active or being reactivated. | |
Type | getType () const |
Returns request type. | |
Result | start (AsyncEventLoop &eventLoop) |
Shortcut for AsyncEventLoop::start. | |
void | setUserFlags (uint16_t externalFlags) |
Sets user flags, holding some meaningful data for the caller. | |
uint16_t | getUserFlags () const |
Gets user flags, holding some meaningful data for the caller. | |
Function< void(AsyncResult &)> * | getCloseCallback () |
Returns currently set close callback (if any) passed to AsyncRequest::stop. | |
const Function< void(AsyncResult &)> * | getCloseCallback () const |
Public Attributes | |
Function< void(Result &)> | callback |
Called after the operation is completed, on the event loop thread. | |
![]() | |
AsyncRequest * | next = nullptr |
AsyncRequest * | prev = nullptr |
Friends | |
struct | AsyncEventLoop |
Additional Inherited Members | |
![]() | |
Result | checkState () |
void | queueSubmission (AsyncEventLoop &eventLoop) |
AsyncTaskSequence * | getTask () |
![]() | |
AsyncSequence * | sequence = nullptr |
Starts an asynchronous file system operation (open, close, read, write, sendFile, stat, lstat, fstat, etc.) Some operations need a file path and others need a file descriptor.
Example of async open operation:
Example of async close operation:
Example of async read operation:
Example of async write operation:
Example of async copy operation:
Example of async copy directory operation:
Example of async rename operation:
Example of async remove empty directory operation:
Example of async remove file operation:
SC::Result SC::AsyncFileSystemOperation::close | ( | AsyncEventLoop & | eventLoop, |
FileDescriptor::Handle | handle ) |
Closes a file descriptor asynchronously.
eventLoop | The event loop to use |
handle | The file descriptor to close // Create Thread Pool where to run fs operations
static constexpr int NUM_THREADS = 1;
ThreadPool threadPool;
SC_TEST_EXPECT(threadPool.create(NUM_THREADS));
// Create Event Loop
AsyncEventLoop eventLoop;
SC_TEST_EXPECT(eventLoop.create(options));
// Create a test file using FileSystem
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
AsyncFileSystemOperation asyncFileSystemOperation;
int callbackCalled = 0;
asyncFileSystemOperation.callback = [&](AsyncFileSystemOperation::Result& res)
{
callbackCalled++;
SC_TEST_EXPECT(res.isValid());
SC_TEST_EXPECT(res.completionData.code == 0);
};
SC_TEST_EXPECT(asyncFileSystemOperation.setThreadPool(threadPool));
FileDescriptor fd;
String path = StringEncoding::Native;
SC_TEST_EXPECT(Path::join(path, {report.applicationRootDirectory, "FileSystemOperationClose.txt"}));
SC_TEST_EXPECT(fd.open(path.view(), FileOpen::Read));
FileDescriptor::Handle handle = FileDescriptor::Invalid;
SC_TEST_EXPECT(fd.get(handle, Result::Error("Invalid FD")));
fd.detach();
SC_TEST_EXPECT(asyncFileSystemOperation.close(eventLoop, handle));
SC_TEST_EXPECT(eventLoop.run());
SC_TEST_EXPECT(callbackCalled == 1);
// Remove test files
SC_TEST_EXPECT(fs.removeFile("FileSystemOperationClose.txt"));
|
SC::Result SC::AsyncFileSystemOperation::copyDirectory | ( | AsyncEventLoop & | eventLoop, |
StringSpan | path, | ||
StringSpan | destinationPath, | ||
FileSystemCopyFlags | copyFlags = FileSystemCopyFlags() ) |
Copies a directory from one location to another.
eventLoop | The event loop to use |
path | The path to the source directory |
destinationPath | The path to the destination directory |
copyFlags | Flags to control the copy operation // Create Thread Pool where to run fs operations
static constexpr int NUM_THREADS = 1;
ThreadPool threadPool;
SC_TEST_EXPECT(threadPool.create(NUM_THREADS));
// Create Event Loop
AsyncEventLoop eventLoop;
SC_TEST_EXPECT(eventLoop.create(options));
// Create a test directory structure synchronously
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
SC_TEST_EXPECT(fs.makeDirectory("AsyncCopyDir"));
SC_TEST_EXPECT(fs.makeDirectory("AsyncCopyDir/subdir"));
// Prepare async copy operation
AsyncFileSystemOperation asyncFileSystemOperation;
asyncFileSystemOperation.callback = [&](AsyncFileSystemOperation::Result& res)
{
SC_TEST_EXPECT(res.isValid());
SC_TEST_EXPECT(res.completionData.code == 0);
};
SC_TEST_EXPECT(asyncFileSystemOperation.setThreadPool(threadPool));
// Copy the directory
String sourcePath = StringEncoding::Native;
String destPath = StringEncoding::Native;
SC_TEST_EXPECT(Path::join(sourcePath, {report.applicationRootDirectory, "AsyncCopyDir"}));
SC_TEST_EXPECT(Path::join(destPath, {report.applicationRootDirectory, "AsyncCopyDirCopy"}));
SC_TEST_EXPECT(asyncFileSystemOperation.copyDirectory(eventLoop, sourcePath.view(), destPath.view()));
SC_TEST_EXPECT(eventLoop.run());
// Verify the content was copied correctly
SC_TEST_EXPECT(fs.existsAndIsFile("AsyncCopyDirCopy/file1.txt"));
SC_TEST_EXPECT(fs.existsAndIsFile("AsyncCopyDirCopy/subdir/file2.txt"));
String text = StringEncoding::Ascii;
SC_TEST_EXPECT(fs.read("AsyncCopyDirCopy/file1.txt", text));
SC_TEST_EXPECT(text.view() == "data1");
SC_TEST_EXPECT(fs.read("AsyncCopyDirCopy/subdir/file2.txt", text));
SC_TEST_EXPECT(text.view() == "data2");
// Cleanup
SC_TEST_EXPECT(fs.removeFile("AsyncCopyDir/file1.txt"));
SC_TEST_EXPECT(fs.removeFile("AsyncCopyDir/subdir/file2.txt"));
SC_TEST_EXPECT(fs.removeEmptyDirectory("AsyncCopyDir/subdir"));
SC_TEST_EXPECT(fs.removeEmptyDirectory("AsyncCopyDir"));
SC_TEST_EXPECT(fs.removeFile("AsyncCopyDirCopy/file1.txt"));
SC_TEST_EXPECT(fs.removeFile("AsyncCopyDirCopy/subdir/file2.txt"));
SC_TEST_EXPECT(fs.removeEmptyDirectory("AsyncCopyDirCopy/subdir"));
SC_TEST_EXPECT(fs.removeEmptyDirectory("AsyncCopyDirCopy"));
|
SC::Result SC::AsyncFileSystemOperation::copyFile | ( | AsyncEventLoop & | eventLoop, |
StringSpan | path, | ||
StringSpan | destinationPath, | ||
FileSystemCopyFlags | copyFlags = FileSystemCopyFlags() ) |
Copies a file from one location to another.
eventLoop | The event loop to use |
path | The path to the source file |
destinationPath | The path to the destination file |
copyFlags | Flags to control the copy operation // Create Thread Pool where to run fs operations
static constexpr int NUM_THREADS = 1;
ThreadPool threadPool;
SC_TEST_EXPECT(threadPool.create(NUM_THREADS));
// Create Event Loop
AsyncEventLoop eventLoop;
SC_TEST_EXPECT(eventLoop.create(options));
// Create a test file using FileSystem
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
AsyncFileSystemOperation asyncFileSystemOperation;
asyncFileSystemOperation.callback = [&](AsyncFileSystemOperation::Result& res)
{
SC_TEST_EXPECT(res.isValid());
SC_TEST_EXPECT(res.completionData.code == 0);
};
SC_TEST_EXPECT(asyncFileSystemOperation.setThreadPool(threadPool));
// Copy the file
String sourcePath = StringEncoding::Native;
String destPath = StringEncoding::Native;
SC_TEST_EXPECT(Path::join(sourcePath, {report.applicationRootDirectory, "FileSystemOperationCopy.txt"}));
SC_TEST_EXPECT(Path::join(destPath, {report.applicationRootDirectory, "FileSystemOperationCopy2.txt"}));
SC_TEST_EXPECT(asyncFileSystemOperation.copyFile(eventLoop, sourcePath.view(), destPath.view()));
SC_TEST_EXPECT(eventLoop.run());
// Verify the content was copied correctly
FileDescriptor verifyFd;
SC_TEST_EXPECT(verifyFd.open(destPath.view(), FileOpen::Read));
String text;
SC_TEST_EXPECT(verifyFd.readUntilEOF(text));
SC_TEST_EXPECT(text.view() == "FileSystemOperationCopy");
SC_TEST_EXPECT(verifyFd.close()); // Close before removing it
// Remove test files
SC_TEST_EXPECT(fs.removeFile(sourcePath.view()));
SC_TEST_EXPECT(fs.removeFile(destPath.view()));
|
SC::Result SC::AsyncFileSystemOperation::open | ( | AsyncEventLoop & | eventLoop, |
StringSpan | path, | ||
FileOpen | mode ) |
Opens a file asynchronously and returns its corresponding file descriptor.
eventLoop | The event loop to use |
path | The path to the file to open (encoded in UTF-8 on Posix and UTF-16 on Windows) |
mode | The mode to open the file in (read, write, read-write, etc.) // Create Thread Pool where to run fs operations
static constexpr int NUM_THREADS = 1;
ThreadPool threadPool;
SC_TEST_EXPECT(threadPool.create(NUM_THREADS));
// Create Event Loop
AsyncEventLoop eventLoop;
SC_TEST_EXPECT(eventLoop.create(options));
// Create a test file using FileSystem
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
AsyncFileSystemOperation asyncFileSystemOperation;
asyncFileSystemOperation.callback = [&](AsyncFileSystemOperation::Result& res)
{
SC_TEST_EXPECT(res.isValid());
SC_TEST_EXPECT(res.completionData.code == 0);
SC_TEST_EXPECT(res.completionData.handle != FileDescriptor::Invalid);
// Read the file content from the file descriptor handle (that is already opened)
// and check that the content is correct. Descriptor is closed automatically by File.
FileDescriptor fd(res.completionData.handle);
String text;
SC_TEST_EXPECT(fd.readUntilEOF(text));
SC_TEST_EXPECT(text.view() == "FileSystemOperationOpen");
};
// Set the thread pool where the open operation will be run
SC_TEST_EXPECT(asyncFileSystemOperation.setThreadPool(threadPool));
// Start the open operation on the given file
// IMPORTANT! The path string passed in must be in Native Encoding (that means UTF16 on Windows)
String path = StringEncoding::Native;
SC_TEST_EXPECT(Path::join(path, {report.applicationRootDirectory, "FileSystemOperationOpen.txt"}));
SC_TEST_EXPECT(asyncFileSystemOperation.open(eventLoop, path.view(), FileOpen::Read));
SC_TEST_EXPECT(eventLoop.run());
// Remove test files
SC_TEST_EXPECT(fs.removeFile("FileSystemOperationOpen.txt"));
|
SC::Result SC::AsyncFileSystemOperation::read | ( | AsyncEventLoop & | eventLoop, |
FileDescriptor::Handle | handle, | ||
Span< char > | buffer, | ||
uint64_t | offset ) |
Reads data from a file descriptor at a given offset.
eventLoop | The event loop to use |
handle | The file descriptor to read from |
buffer | The buffer to read into |
offset | The offset in the file to read from // Create Thread Pool where to run fs operations
static constexpr int NUM_THREADS = 1;
ThreadPool threadPool;
SC_TEST_EXPECT(threadPool.create(NUM_THREADS));
// Create Event Loop
AsyncEventLoop eventLoop;
SC_TEST_EXPECT(eventLoop.create(options));
// Create a test file using FileSystem
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
// Open the file first
FileDescriptor fd;
String path = StringEncoding::Native;
SC_TEST_EXPECT(Path::join(path, {report.applicationRootDirectory, "FileSystemOperationRead.txt"}));
SC_TEST_EXPECT(fd.open(path.view(), FileOpen::Read));
FileDescriptor::Handle handle = FileDescriptor::Invalid;
SC_TEST_EXPECT(fd.get(handle, Result::Error("Invalid FD")));
fd.detach();
AsyncFileSystemOperation asyncFileSystemOperation;
asyncFileSystemOperation.callback = [&](AsyncFileSystemOperation::Result& res)
{
SC_TEST_EXPECT(res.isValid());
SC_TEST_EXPECT(res.completionData.numBytes == 23); // Length of "FileSystemOperationRead"
};
SC_TEST_EXPECT(asyncFileSystemOperation.setThreadPool(threadPool));
// Read from the file
char buffer[32] = {0};
SC_TEST_EXPECT(asyncFileSystemOperation.read(eventLoop, handle, Span<char>(buffer, sizeof(buffer)), 0));
SC_TEST_EXPECT(eventLoop.run());
StringView readContent({buffer, 23}, true, StringEncoding::Ascii);
SC_TEST_EXPECT(readContent == StringView("FileSystemOperationRead"));
SC_TEST_EXPECT(eventLoop.run());
// Remove test files
SC_TEST_EXPECT(fs.removeFile("FileSystemOperationRead.txt"));
|
SC::Result SC::AsyncFileSystemOperation::removeEmptyDirectory | ( | AsyncEventLoop & | eventLoop, |
StringSpan | path ) |
Removes a directory asynchronously.
eventLoop | The event loop to use |
path | The path to the directory to remove |
SC::Result SC::AsyncFileSystemOperation::removeFile | ( | AsyncEventLoop & | eventLoop, |
StringSpan | path ) |
Removes a file asynchronously.
eventLoop | The event loop to use |
path | The path to the file to remove // Create Thread Pool where to run fs operations
static constexpr int NUM_THREADS = 1;
ThreadPool threadPool;
SC_TEST_EXPECT(threadPool.create(NUM_THREADS));
// Create Event Loop
AsyncEventLoop eventLoop;
SC_TEST_EXPECT(eventLoop.create(options));
// Create a test file using FileSystem
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
String filePath = StringEncoding::Native;
SC_TEST_EXPECT(Path::join(filePath, {report.applicationRootDirectory, "FileSystemOperationRemoveFile.txt"}));
SC_TEST_EXPECT(fs.writeString(filePath.view(), "FileSystemOperationRemoveFile"));
AsyncFileSystemOperation asyncFileSystemOperation;
asyncFileSystemOperation.callback = [&](AsyncFileSystemOperation::Result& res)
{
SC_TEST_EXPECT(res.isValid());
SC_TEST_EXPECT(res.completionData.code == 0);
};
SC_TEST_EXPECT(asyncFileSystemOperation.setThreadPool(threadPool));
// Remove the file
SC_TEST_EXPECT(asyncFileSystemOperation.removeFile(eventLoop, filePath.view()));
SC_TEST_EXPECT(eventLoop.run());
// Verify the file was removed
SC_TEST_EXPECT(not fs.existsAndIsFile(filePath.view()));
|
SC::Result SC::AsyncFileSystemOperation::rename | ( | AsyncEventLoop & | eventLoop, |
StringSpan | path, | ||
StringSpan | newPath ) |
Renames a file.
eventLoop | The event loop to use |
path | The path to the source file |
newPath | The new path to the file // Create Thread Pool where to run fs operations
static constexpr int NUM_THREADS = 1;
ThreadPool threadPool;
SC_TEST_EXPECT(threadPool.create(NUM_THREADS));
// Create Event Loop
AsyncEventLoop eventLoop;
SC_TEST_EXPECT(eventLoop.create(options));
// Create a test file using FileSystem
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
AsyncFileSystemOperation asyncFileSystemOperation;
asyncFileSystemOperation.callback = [&](AsyncFileSystemOperation::Result& res)
{
SC_TEST_EXPECT(res.isValid());
SC_TEST_EXPECT(res.completionData.code == 0);
};
SC_TEST_EXPECT(asyncFileSystemOperation.setThreadPool(threadPool));
// Rename the file
String sourcePath = StringEncoding::Native;
String destPath = StringEncoding::Native;
SC_TEST_EXPECT(Path::join(sourcePath, {report.applicationRootDirectory, "FileSystemOperationRename.txt"}));
SC_TEST_EXPECT(Path::join(destPath, {report.applicationRootDirectory, "FileSystemOperationRename2.txt"}));
SC_TEST_EXPECT(asyncFileSystemOperation.rename(eventLoop, sourcePath.view(), destPath.view()));
SC_TEST_EXPECT(eventLoop.run());
// Verify the content was renamed correctly
FileDescriptor verifyFd;
SC_TEST_EXPECT(verifyFd.open(destPath.view(), FileOpen::Read));
String text;
SC_TEST_EXPECT(verifyFd.readUntilEOF(text));
SC_TEST_EXPECT(text.view() == "FileSystemOperationRename");
SC_TEST_EXPECT(verifyFd.close()); // Close before removing it
// Remove test files
SC_TEST_EXPECT(fs.removeFile(destPath.view()));
|
SC::Result SC::AsyncFileSystemOperation::setThreadPool | ( | ThreadPool & | threadPool | ) |
Sets the thread pool to use for the operation.
SC::Result SC::AsyncFileSystemOperation::write | ( | AsyncEventLoop & | eventLoop, |
FileDescriptor::Handle | handle, | ||
Span< const char > | buffer, | ||
uint64_t | offset ) |
Writes data to a file descriptor at a given offset.
eventLoop | The event loop to use |
handle | The file descriptor to write to |
buffer | The buffer containing data to write |
offset | The offset in the file to write to // Create Thread Pool where to run fs operations
static constexpr int NUM_THREADS = 1;
ThreadPool threadPool;
SC_TEST_EXPECT(threadPool.create(NUM_THREADS));
// Create Event Loop
AsyncEventLoop eventLoop;
SC_TEST_EXPECT(eventLoop.create(options));
// Open the file first
FileDescriptor fd;
String path = StringEncoding::Native;
SC_TEST_EXPECT(Path::join(path, {report.applicationRootDirectory, "FileSystemOperationWrite.txt"}));
SC_TEST_EXPECT(fd.open(path.view(), FileOpen::Write));
FileDescriptor::Handle handle = FileDescriptor::Invalid;
SC_TEST_EXPECT(fd.get(handle, Result::Error("Invalid FD")));
fd.detach();
AsyncFileSystemOperation asyncFileSystemOperation;
asyncFileSystemOperation.callback = [&](AsyncFileSystemOperation::Result& res)
{
SC_TEST_EXPECT(res.isValid());
SC_TEST_EXPECT(res.completionData.numBytes == 24); // Length of "FileSystemOperationWrite"
};
SC_TEST_EXPECT(asyncFileSystemOperation.setThreadPool(threadPool));
// Write to the file
const char* writeData = "FileSystemOperationWrite";
SC_TEST_EXPECT(asyncFileSystemOperation.write(eventLoop, handle, Span<const char>(writeData, 24), 0));
SC_TEST_EXPECT(eventLoop.run());
// Verify the content was written correctly
FileDescriptor verifyFd;
SC_TEST_EXPECT(verifyFd.open(path.view(), FileOpen::Read));
String text;
SC_TEST_EXPECT(verifyFd.readUntilEOF(text));
SC_TEST_EXPECT(text.view() == "FileSystemOperationWrite");
SC_TEST_EXPECT(verifyFd.close()); // Close before removing it
// Remove test files
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
SC_TEST_EXPECT(fs.removeFile("FileSystemOperationWrite.txt"));
|
Called after the operation is completed, on the event loop thread.