17.1.4 Multithreaded Transactions
It is perfectly legal to create processes and threads while holding
a lock or, for that matter, from within an RCU read-side critical
section.
Not only is it legal, but it is quite simple, as can be seen from the
following code fragment:
1 pthread_mutex_lock(...);
2 for (i = 0; i < ncpus; i++)
3 tid[i] = pthread_create(...);
4 for (i = 0; i < ncpus; i++)
5 pthread_join(tid[i], ...)
6 pthread_mutex_unlock(...);
|
This pseudo-code fragment uses pthread_create() to spawn one thread
per CPU, then uses pthread_join() to wait for each to complete,
all under the protection of pthread_mutex_lock().
The effect is to execute a lock-based critical section in parallel,
and one could obtain a similar effect using fork() and wait().
Of course, the critical section would need to be quite large to justify
the thread-spawning overhead, but there are many examples of large
critical sections in production software.
What might TM do about thread spawning within a transaction?
- Declare pthread_create() to be illegal within transactions,
resulting in transaction abort (preferred) or undefined
behavior. Alternatively, enlist the compiler to enforce
pthread_create()-free transactions.
- Permit pthread_create() to be executed within a
transaction, but only the parent thread will be considered to
be part of the transaction.
This approach seems to be reasonably compatible with existing and
posited TM implementations, but seems to be a trap for the unwary.
This approach raises further questions, such as how to handle
conflicting child-thread accesses.
- Convert the pthread_create()s to function calls.
This approach is also an attractive nuisance, as it does not
handle the not-uncommon cases where the child threads communicate
with one another.
In addition, it does not permit parallel execution of the body
of the transaction.
- Extend the transaction to cover the parent and all child threads.
This approach raises interesting questions about the nature of
conflicting accesses, given that the parent and children are
presumably permitted to conflict with each other, but not with
other threads.
It also raises interesting questions as to what should happen
if the parent thread does not wait for its children before
committing the transaction.
Even more interesting, what happens if the parent conditionally
executes pthread_join() based on the values of variables
participating in the transaction?
The answers to these questions are reasonably straightforward
in the case of locking.
The answers for TM are left as an exercise for the reader.
Given that parallel execution of transactions is commonplace in the
database world, it is perhaps surprising that current TM proposals do
not provide for it.
On the other hand, the example above is a fairly sophisticated use
of locking that is not normally found in simple textbook examples,
so perhaps its omission is to be expected.
That said, there are rumors that some TM researchers are investigating
fork/join parallelism within transactions, so perhaps this topic will
soon be addressed more thoroughly.
Paul E. McKenney
2011-12-16