17.1.1 I/O Operations
One can execute I/O operations within a lock-based critical section,
and, at least in principle, from within an RCU read-side critical section.
What happens when you attempt to execute an I/O operation from within
a transaction?
The underlying problem is that transactions may be rolled back, for
example, due to conflicts.
Roughly speaking, this requires that all operations within any given
transaction be idempotent, so that executing the operation twice has
the same effect as executing it once.
Unfortunately, I/O is in general the prototypical non-idempotent
operation, making it difficult to include general I/O operations in
transactions.
Here are some options for handling of I/O within transactions:
- Restrict I/O within transactions to buffered I/O with in-memory
buffers.
These buffers may then be included in the transaction in the
same way that any other memory location might be included.
This seems to be the mechanism of choice, and it does work
well in many common cases of situations such as stream I/O and
mass-storage I/O.
However, special handling is required in cases where multiple
record-oriented output streams are merged onto a single file
from multiple processes, as might be done using the ``a+''
option to fopen() or the O_APPEND flag to open().
In addition, as will be seen in the next section, common
networking operations cannot be handled via buffering.
- Prohibit I/O within transactions, so that any attempt to execute
an I/O operation aborts the enclosing transaction (and perhaps
multiple nested transactions).
This approach seems to be the conventional TM approach for
unbuffered I/O, but requires that TM interoperate with other
synchronization primitives that do tolerate I/O.
- Prohibit I/O within transactions, but enlist the compiler's aid
in enforcing this prohibition.
- Permit only one special
``inevitable'' transaction [SMS08]
to proceed
at any given time, thus allowing inevitable transactions to
contain I/O operations.
This works in general, but severely limits the scalability and
performance of I/O operations.
Given that scalability and performance is a first-class goal of
parallelism, this approach's generality seems a bit self-limiting.
Worse yet, use of inevitability to tolerate I/O operations
seems to prohibit use of manual transaction-abort operations.17.1
- Create new hardware and protocols such that I/O operations can
be pulled into the transactional substrate.
In the case of input operations, the hardware would need to
correctly predict the result of the operation, and to abort the
transaction if the prediction failed.
I/O operations are a well-known weakness of TM, and it is not clear
that the problem of supporting I/O in transactions has a reasonable
general solution, at least if ``reasonable'' is to include usable
performance and scalability.
Nevertheless, continued time and attention to this problem will likely
produce additional progress.
Paul E. McKenney
2011-12-16