Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
SC::AsyncFileSystemOperation Struct Reference

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>

Inheritance diagram for SC::AsyncFileSystemOperation:
SC::AsyncRequest

Public Types

enum class  Operation {
  None = 0 ,
  Open ,
  Close ,
  Read ,
  Write ,
  CopyFile ,
  CopyDirectory ,
  Rename ,
  RemoveDirectory ,
  RemoveFile
}
 
using CompletionData = AsyncFileSystemOperationCompletionData
 
using Result = AsyncResultOf<AsyncFileSystemOperation, CompletionData>
 
- Public Types inherited from SC::AsyncRequest
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.
 
- Public Member Functions inherited from SC::AsyncRequest
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.
 
- Public Attributes inherited from SC::AsyncRequest
AsyncRequestnext = nullptr
 
AsyncRequestprev = nullptr
 

Friends

struct AsyncEventLoop
 

Additional Inherited Members

- Protected Member Functions inherited from SC::AsyncRequest
Result checkState ()
 
void queueSubmission (AsyncEventLoop &eventLoop)
 
AsyncTaskSequencegetTask ()
 
- Protected Attributes inherited from SC::AsyncRequest
AsyncSequencesequence = nullptr
 

Detailed Description

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.

Note
Operations will run on the thread pool set with AsyncFileSystemOperation::setThreadPool on all backends except when the event loop is using io_uring on Linux.
Warning
File paths must be encoded in the native encoding of the OS, that is UTF-8 on Posix and UTF-16 on Windows.

Example of async open 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));
SC_TEST_EXPECT(fs.writeString("FileSystemOperationOpen.txt", "FileSystemOperationOpen"));
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"));

Example of async close 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));
SC_TEST_EXPECT(fs.writeString("FileSystemOperationClose.txt", "FileSystemOperationClose"));
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"));

Example of async read 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));
SC_TEST_EXPECT(fs.writeString("FileSystemOperationRead.txt", "FileSystemOperationRead"));
// 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"));

Example of async write 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));
// 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"));

Example of async 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));
SC_TEST_EXPECT(fs.writeString("FileSystemOperationCopy.txt", "FileSystemOperationCopy"));
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()));

Example of async copy directory 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.writeString("AsyncCopyDir/file1.txt", "data1"));
SC_TEST_EXPECT(fs.makeDirectory("AsyncCopyDir/subdir"));
SC_TEST_EXPECT(fs.writeString("AsyncCopyDir/subdir/file2.txt", "data2"));
// 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"));

Example of async rename 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));
SC_TEST_EXPECT(fs.writeString("FileSystemOperationRename.txt", "FileSystemOperationRename"));
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()));

Example of async remove empty directory 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 using FileSystem
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
String dirPath = StringEncoding::Native;
SC_TEST_EXPECT(Path::join(dirPath, {report.applicationRootDirectory, "FileSystemOperationRemoveEmptyDirectory"}));
SC_TEST_EXPECT(fs.makeDirectory(dirPath.view()));
AsyncFileSystemOperation asyncFileSystemOperation;
int numCallbacks = 0;
asyncFileSystemOperation.callback = [&](AsyncFileSystemOperation::Result& res)
{
SC_TEST_EXPECT(res.isValid());
SC_TEST_EXPECT(res.completionData.code == 0);
numCallbacks++;
};
SC_TEST_EXPECT(asyncFileSystemOperation.setThreadPool(threadPool));
// Remove the empty directory
SC_TEST_EXPECT(asyncFileSystemOperation.removeEmptyDirectory(eventLoop, dirPath.view()));
SC_TEST_EXPECT(eventLoop.run());
SC_TEST_EXPECT(numCallbacks == 1); // Ensure the callback was called
// Verify the directory was removed
SC_TEST_EXPECT(!fs.existsAndIsDirectory(dirPath.view()));

Example of async remove file 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));
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()));

Member Function Documentation

◆ close()

SC::Result SC::AsyncFileSystemOperation::close ( AsyncEventLoop & eventLoop,
FileDescriptor::Handle handle )

Closes a file descriptor asynchronously.

Parameters
eventLoopThe event loop to use
handleThe 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));
SC_TEST_EXPECT(fs.writeString("FileSystemOperationClose.txt", "FileSystemOperationClose"));
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"));

◆ copyDirectory()

SC::Result SC::AsyncFileSystemOperation::copyDirectory ( AsyncEventLoop & eventLoop,
StringSpan path,
StringSpan destinationPath,
FileSystemCopyFlags copyFlags = FileSystemCopyFlags() )

Copies a directory from one location to another.

Parameters
eventLoopThe event loop to use
pathThe path to the source directory
destinationPathThe path to the destination directory
copyFlagsFlags 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.writeString("AsyncCopyDir/file1.txt", "data1"));
SC_TEST_EXPECT(fs.makeDirectory("AsyncCopyDir/subdir"));
SC_TEST_EXPECT(fs.writeString("AsyncCopyDir/subdir/file2.txt", "data2"));
// 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"));

◆ copyFile()

SC::Result SC::AsyncFileSystemOperation::copyFile ( AsyncEventLoop & eventLoop,
StringSpan path,
StringSpan destinationPath,
FileSystemCopyFlags copyFlags = FileSystemCopyFlags() )

Copies a file from one location to another.

Parameters
eventLoopThe event loop to use
pathThe path to the source file
destinationPathThe path to the destination file
copyFlagsFlags 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));
SC_TEST_EXPECT(fs.writeString("FileSystemOperationCopy.txt", "FileSystemOperationCopy"));
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()));

◆ open()

SC::Result SC::AsyncFileSystemOperation::open ( AsyncEventLoop & eventLoop,
StringSpan path,
FileOpen mode )

Opens a file asynchronously and returns its corresponding file descriptor.

Parameters
eventLoopThe event loop to use
pathThe path to the file to open (encoded in UTF-8 on Posix and UTF-16 on Windows)
modeThe 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));
SC_TEST_EXPECT(fs.writeString("FileSystemOperationOpen.txt", "FileSystemOperationOpen"));
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"));

◆ read()

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.

Parameters
eventLoopThe event loop to use
handleThe file descriptor to read from
bufferThe buffer to read into
offsetThe 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));
SC_TEST_EXPECT(fs.writeString("FileSystemOperationRead.txt", "FileSystemOperationRead"));
// 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"));

◆ removeEmptyDirectory()

SC::Result SC::AsyncFileSystemOperation::removeEmptyDirectory ( AsyncEventLoop & eventLoop,
StringSpan path )

Removes a directory asynchronously.

Parameters
eventLoopThe event loop to use
pathThe path to the directory to remove
Note
The directory must be empty for this operation to succeed
// 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 using FileSystem
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
String dirPath = StringEncoding::Native;
SC_TEST_EXPECT(Path::join(dirPath, {report.applicationRootDirectory, "FileSystemOperationRemoveEmptyDirectory"}));
SC_TEST_EXPECT(fs.makeDirectory(dirPath.view()));
AsyncFileSystemOperation asyncFileSystemOperation;
int numCallbacks = 0;
asyncFileSystemOperation.callback = [&](AsyncFileSystemOperation::Result& res)
{
SC_TEST_EXPECT(res.isValid());
SC_TEST_EXPECT(res.completionData.code == 0);
numCallbacks++;
};
SC_TEST_EXPECT(asyncFileSystemOperation.setThreadPool(threadPool));
// Remove the empty directory
SC_TEST_EXPECT(asyncFileSystemOperation.removeEmptyDirectory(eventLoop, dirPath.view()));
SC_TEST_EXPECT(eventLoop.run());
SC_TEST_EXPECT(numCallbacks == 1); // Ensure the callback was called
// Verify the directory was removed
SC_TEST_EXPECT(!fs.existsAndIsDirectory(dirPath.view()));

◆ removeFile()

SC::Result SC::AsyncFileSystemOperation::removeFile ( AsyncEventLoop & eventLoop,
StringSpan path )

Removes a file asynchronously.

Parameters
eventLoopThe event loop to use
pathThe 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()));

◆ rename()

SC::Result SC::AsyncFileSystemOperation::rename ( AsyncEventLoop & eventLoop,
StringSpan path,
StringSpan newPath )

Renames a file.

Parameters
eventLoopThe event loop to use
pathThe path to the source file
newPathThe 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));
SC_TEST_EXPECT(fs.writeString("FileSystemOperationRename.txt", "FileSystemOperationRename"));
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()));

◆ setThreadPool()

SC::Result SC::AsyncFileSystemOperation::setThreadPool ( ThreadPool & threadPool)

Sets the thread pool to use for the operation.

◆ write()

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.

Parameters
eventLoopThe event loop to use
handleThe file descriptor to write to
bufferThe buffer containing data to write
offsetThe 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"));

Member Data Documentation

◆ callback

Function<void(Result&)> SC::AsyncFileSystemOperation::callback

Called after the operation is completed, on the event loop thread.


The documentation for this struct was generated from the following file: