Reading from and Writing to a File

Every open file has a file pointer that specifies the next byte to be read or the location to receive the next byte written. When a file is opened for the first time, Windows places the file pointer at the beginning of the file. As each byte is read or written, Windows advances the file pointer. An application can also move the file pointer by using the SetFilePointer function.

An application reads from and writes to a file by using the ReadFile and WriteFile functions. These functions require a handle of a file to be opened for reading and writing, respectively. ReadFile and WriteFile read and write a specified number of bytes at the location indicated by the file pointer. The data is read and written exactly as given; the functions do not format the data.

When the file pointer reaches the end of a file and the application attempts to read from the file, no error occurs, but no bytes are read. Therefore, reading zero bytes without an error means the program has reached the end of the file. Writing zero bytes does nothing.

An application can truncate or extend a file by using the SetEndOfFile function. This function sets the end of file to the current position of the file pointer.

When an application writes to a file, Windows usually collects the data being written in an internal buffer and writes the data to the disk on a regular basis.

An application can force the operating system to write the contents of the buffer to the disk by using the FlushFileBuffers function. Alternatively, an application can specify that write operations are to bypass the internal buffer and write directly to the disk by setting a flag when the file is created or opened by using the CreateFile function.

If there is data in the internal buffer when the file is closed, Windows does not automatically write the contents of the buffer to the disk before closing the file. If the application does not force the operating system to write the buffer to disk before closing the file, the caching algorithm determines when the buffer is written.

Applications must not read from nor write to the input buffer that a read operation is using until the read operation completes. A premature access to the input buffer may lead to corruption of the data read into that buffer.

Locking and Unlocking Files

Although Windows allows more than one application to open a file and write to it, applications must not write over each other's work. An application can prevent this problem by temporarily locking a region in a file. The LockFile and LockFileEx functions lock a specified range of bytes in a file. The range may extend beyond the current end of the file. Locking part of a file prevents all other processes from reading or writing anywhere in the specified area. Attempts to read from or write to a region locked by another process always fail.

The LockFileEx function allows an application to specify either a shared lock or an exclusive lock. An exclusive lock denies all other processes both read and write access to the specified region of the file. A shared lock denies all processes write access to the specified region of the file, including the process that first locks the region. This can be used to create a read-only region in a file.

The application unlocks the region by using the UnlockFile or UnlockFileEx function. An application should unlock all locked areas before closing a file.

Asynchronous Input and Output

Asynchronous input and output (asynchronous I/O) allows some I/O functions to return immediately, even though an I/O request is still pending. Asynchronous I/O enables an application to continue with other processing and wait for the I/O to be completed at a later time. Asynchronous I/O is also called overlapped I/O.

The ReadFile and WriteFile functions enable an application to specify an OVERLAPPED structure that indicates where to position the file pointer before the read or write operation. The handle of the file being read from or written to must have been opened with the FILE_FLAG_OVERLAPPED flag. You can also create an event and put the handle in the OVERLAPPED structure; the wait functions can then be used to wait for the I/O operation to complete by waiting on the event handle.

An application can also wait on the file handle to synchronize the completion of an I/O operation, but doing so requires extreme caution. Each time an I/O operation is started, the operating system sets the file handle to the nonsignaled state. Each time an I/O operation is completed, the operating system sets the file handle to the signaled state. Therefore, if an application starts two I/O operations and waits on the file handle, there is no way to determine which operation is finished when the handle is set to the signaled state. If an application must perform multiple asynchronous I/O operations on a single file, it should wait on the event handle in the OVERLAPPED structure for each I/O operation, rather than on the file handle.

To cancel all pending asynchronous I/O operations, use the CancelIO function. This function only cancels operations issued by the calling thread for the specified file handle.

The ReadFileEx and WriteFileEx functions enable an application to specify a routine to execute (see FileIOCompletionRoutine) when the asynchronous I/O request is completed.

For more information, see Synchronization and Overlapped Input and Output.

I/O Completion Ports

I/O completion ports are used with asynchronous I/O. The CreateIoCompletionPort function associates an I/O completion port with one or more file handles. When an asynchronous I/O operation started on a file handle associated with an I/O completion port is completed, an I/O completion packet is queued to the port. This can be used to combine the synchronization point for multiple file handles into a single object.

A thread uses the GetQueuedCompletionStatus function to wait for an I/O completion packet to be queued to the I/O completion port, rather than waiting directly for the asynchronous I/O to complete. Threads that block their execution on an I/O completion port are released in last-in-first-out (LIFO) order. This means that when an I/O completion packet is queued to the I/O completion port, the system releases the last thread to block its execution on the port.

The most important property of an I/O completion port is the concurrency value. The concurrency value of an I/O completion port is specified when the I/O completion port is created. This value limits the number of runnable threads associated with the I/O completion port. When the total number of runnable threads associated with the I/O completion port reaches the concurrency value, the system blocks the execution of the threads until the number of runnable threads associated with the I/O completion port drops below the concurrency value. The most efficient scenario occurs when there are I/O completion packets waiting in the queue, but no waits can be satisfied because the port has reached its concurrency limit. In this case, when a running thread calls GetQueuedCompletionStatus, it will immediately pick up the queued I/O completion packet. No context switches will occur, because the running thread is continually picking up I/O completion packets and the other threads are unable to run.

The best value to pick for the concurrency value is the number of CPUs on the machine. If your transaction required a lengthy computation, a larger concurrency value will allow more threads to run. Each transaction will take longer to complete, but more transactions will be processed at the same time. It is easy to experiment with the concurrency value to achieve the best effect for your application.

The PostQueuedCompletionStatus function allows an application to queue its own special-purpose I/O completion packets to the I/O completion port without starting an asynchronous I/O operation. This is useful for notifying worker threads of external events.

The I/O completion port is freed when there are no more references to it. The port handle and every file handle associated with the I/O completion port reference the I/O completion port. All the handles must be closed to free the I/O completion port. To close the port handle, call the CloseHandle function.

Software for developers
Delphi Components
.Net Components
Software for Android Developers
More information resources
MegaDetailed.Net
Unix Manual Pages
Delphi Examples
Databases for Amazon shops developers
Amazon Categories Database
Browse Nodes Database