Atomic File Operation API

API Reference


This API provides an atomic file access mechanism that can be used to perform file operation (specially file write) in atomic fashion.

This API only supports regular files. Attempts to use this API on sockets, devices, etc. results in undefined behavior.

Atomic File Operations

An atomic file operation is an operation that cannot be partially performed. Either the entire operation is performed or the operation fails. Any unclean reboot or power-cut should not lead to corruption or inconsistency of the file. Also when a process is performing an atomic write on a file, other processes should not be able modify that file, i.e. some file locking mechanism should be there.

Use le_atomFile_Open() to open a file for atomic access. This API uses Co-operative File Locking mechanism while opening a file, i.e. if file already has an incompatible lock on it, le_atomFile_Open() will block until it can obtain the lock. File must be closed using le_atomFile_Close() or le_atomFile_Cancel() api. Both le_atomFile_Close() and le_atomFile_Close() closes the file and releases the acquired resources. However, le_atomFile_Close() transfers all changes to disk, le_atomFile_Cancel() no change is reflected on file. A file can be deleted atomically using le_atomFile_Delete() api.

For opening C standard file stream, please see Streams section.

Writing on the file descriptors obtained by this API same as writing to regular file descriptor. That means, any write to a file descriptor using this API doesn't ensure that data is transferred to disk. Data is only transferred to disk when le_atomFile_Close() returns successfully. This behavior is same for file stream as well.

Code fragment illustrating atomic write using file descriptor.

// Atomic write example, File Descriptor case.
 
int fd = le_atomFile_Open("./myfile.txt", LE_FLOCK_READ_AND_APPEND);
 
if (fd < 0)
{
// Print error message and exit.
}
 
// Write something in fd
char myString[] = "This string for atomic writing";
 
// Now write this string to fd
write(fd, myString, sizeof(myString)); // This string write doesn't go disk
 
 
le_result_t result = le_atomFile_Close(fd); // Transfers all changes to disk
 
if (result == LE_OK)
{
// Print success message
}

Code fragment illustrating atomic write using file stream.

// Atomic write example, File Stream case.
 
FILE* file = le_atomFile_OpenStream("./myfile.txt", LE_FLOCK_READ_AND_APPEND, NULL);
 
if (file == NULL)
{
// Print error message and exit.
}
 
// Write something in file stream
char myString[] = "This string for atomic writing";
 
// Now write this string to file stream
fwrite(myString, 1, sizeof(myString), file); // This string write doesn't go disk
 
 
le_result_t result = le_atomFile_CloseStream(fd); // Transfers all changes to disk
 
if (result == LE_OK)
{
// Print success message
}

An example illustrating usage of le_atomFile_Close(), le_atomFile_Cancel() and le_atomFile_Delete() function.

int fd = le_atomFile_Open("./myfile.txt", LE_FLOCK_READ_AND_APPEND);
 
if (fd < 0)
{
// Print error message and exit.
}
 
// Write something in fd
char myString[] = "This string for atomic writing";
 
// Now write this string to fd
write(fd, myString, sizeof(myString)); // This string write doesn't go disk
 
bool doCommit = NeedToCommit(); // A fictitious function that returns whether
// write on fd should be sent to disk or not.
 
if (doCommit)
{
le_result_t result = le_atomFile_Close(fd); // Transfer all changes to disk and close
// the file descriptor.
if (result != LE_OK)
{
// Print error message.
}
 
}
else
{
le_atomFile_Cancel(fd); // Discard all changes and close the file descriptor.
}
 
 
// Now do some additional stuff with file myfile.txt
// .........Code.........
// .........Code.........
 
 
// Now delete file myfile.txt
le_result_t result = le_atomFile_Delete("./myfile.txt");
if (result != LE_OK)
{
// Print error message.
}

The le_atomFile_Create() function can be used to create, lock and open a file in one function call.

Streams

The functions le_atomFile_OpenStream() and le_atomFile_CreateStream() can be used to obtain a file stream for atomic operation. le_atomFile_CloseStream() is used to commit all changes to disk and close the stream. le_atomFile_CancelStream() is used to discard all changes and close the stream. These functions are analogous to le_atomFile_Open(), le_atomFile_Create() le_atomFile_Close() and le_atomFile_Cancel() except that works on file streams rather than file descriptors.

Non-blocking

Functions le_atomFile_Open(), le_atomFile_Create(), le_atomFile_OpenStream(), le_atomFile_CreateStream() and le_atomFile_Delete() always block if there is an incompatible lock on the file. Functions le_atomFile_TryOpen(), le_atomFile_TryCreate(), le_atomFile_TryOpenStream(), le_atomFile_TryCreateStream() and le_atomFile_TryDelete() are their non-blocking counterparts.

Multiple Threads

All the functions in this API are thread-safe and reentrant.

Limitations

These APIs have inherent limitations of File Locking API (i.e. advisory lock, inability to detect deadlock etc.), as they use File Locking API.

File descriptors obtained via calling these APIs can't be replicated via fork or dup

int oldfd = le_atomFile_Open("./myfile.txt", LE_FLOCK_READ_AND_APPEND);
 
if (oldfd < 0)
{
// Print error message and exit.
}
 
int newfd = dup(oldfd); // newfd is created via dup.
..
// Write something in newfd
..
le_result_t result = le_atomFile_Close(newfd); // Wrong. This newfd is not recognized by API

File descriptors/streams obtained via using these APIs must be closed using the corresponding closing APIs (i.e. le_atomFile_Close(), le_atomFile_CloseStream() etc.). This is illustrated in following code fragments.

Code fragment showing proper closing procedure of file descriptor obtained via this API.

int fd = le_atomFile_Open("./myfile.txt", LE_FLOCK_READ_AND_APPEND);
 
if (fd < 0)
{
// Print error message and exit.
}
 
..
// Write something in fd
..
le_result_t result = le_atomFile_Close(fd); // This is right.
 
if (result != LE_OK)
{
// Print some error message
}

Code fragment showing wrong closing procedure of file descriptor obtained via this API.

int fd = le_atomFile_Open("./myfile.txt", LE_FLOCK_READ_AND_APPEND);
 
if (fd < 0)
{
// Print error message and exit.
}
 
..
// Write something in fd
..
int result = close(fd); // Wrong as it doesn't use closing API (i.e. le_atomFile_Close()
// or le_atomFile_Cancel())
 
if (result < 0)
{
// Print some error message
}