Atomic File Operation API
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.if (fd < 0){// Print error message and exit.}// Write something in fdchar myString[] = "This string for atomic writing";// Now write this string to fdwrite(fd, myString, sizeof(myString)); // This string write doesn't go diskif (result == LE_OK){// Print success message}
Code fragment illustrating atomic write using file stream.
// Atomic write example, File Stream case.if (file == NULL){// Print error message and exit.}// Write something in file streamchar myString[] = "This string for atomic writing";// Now write this string to file streamfwrite(myString, 1, sizeof(myString), file); // This string write doesn't go diskif (result == LE_OK){// Print success message}
An example illustrating usage of le_atomFile_Close(), le_atomFile_Cancel() and le_atomFile_Delete() function.
if (fd < 0){// Print error message and exit.}// Write something in fdchar myString[] = "This string for atomic writing";// Now write this string to fdwrite(fd, myString, sizeof(myString)); // This string write doesn't go diskbool doCommit = NeedToCommit(); // A fictitious function that returns whether// write on fd should be sent to disk or not.if (doCommit){// 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.txtif (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
if (oldfd < 0){// Print error message and exit.}int newfd = dup(oldfd); // newfd is created via dup...// Write something in newfd..
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.
if (fd < 0){// Print error message and exit.}..// Write something in fd..if (result != LE_OK){// Print some error message}
Code fragment showing wrong closing procedure of file descriptor obtained via this API.
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}
Copyright (C) Sierra Wireless Inc.