3

Say we write some new class which can be used concurrently or not. Obviously, we don't want to have locks on everything on the chance that they will be called concurrently. One way to address this is through parameterizing by mixins specifying locking:

template<class Locking>
struct foo : private Locking {
    void bar() {
        Locking::read_lock();
        // Do something.
        Locking::read_unlock();
    }
};

and instantiating Locking with a class that actually locks for the multithreading case, and with a class that does no-ops for the other case (hopefully, the compiler will even optimize away the calls).

Now suppose I'd like to do this with software-transactional memory instead of locking. Looking at N3919 (or the gcc precursor), the idea is different. There are no calls such as

transaction_start();

transaction_end();

Instead there are function specifiers like

void bar() transaction_safe;

and block specifiers like

transaction { /* body */ }

with strict rules of the latter calling the former, and nothing that looks like it can be used by mixins.

How can this be done(without involving the preprocessor)? Note also that one of the main benefits of STM is composability, but there seems no way to get the instantiation to reflect that bar is transactionable.

Ami Tavory
  • 74,578
  • 11
  • 141
  • 185

1 Answers1

1

In a similar way, with lambda, it seems you may do something like:

template<class Transaction>
struct foo {
    void bar() {
        Transaction::run([](){ /* Do something. */ });
    }
};

with the 2 implementations

template<typename F>
void TransactionNone::run(F f) { f(); }

and

template<typename F>
void TransactionReal::run(F f) { transaction{ f(); } }

For attributes,

A function is transaction-safe if it is not transaction-unsafe.

So it seems you may omit that keyword, and let the compiler/linker do that job.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Many thanks. Getting `Locking` to "wrap" the rest of the code is very clever. For the other point, the quote "A function is transaction-safe if it is not transaction-unsafe" is the key, here. Could you perhaps link to a source for that? I can't seem to find it. – Ami Tavory Aug 09 '16 at 20:38
  • From your Doc N3919, "5 Transaction-Safety for Functions", 2nd note. – Jarod42 Aug 09 '16 at 20:51
  • Many thanks again. So the first part, where `Locking` wraps the code, clinches that issue. As to the second part, I certainly appreciate your pointing out that the default is safe (and it certainly indicates that safe might not that be expensive, and "the compiler [can] do that job" as you write). In a certain (possibly much minor) sense, though, it seems to just flip the question - how can the mixin indicate to the compiler that the function is *unsafe*. Great answer, though. – Ami Tavory Aug 09 '16 at 21:09
  • Marking `TransactionNone::run` `unsafe` should propagate, as I understand. – Jarod42 Aug 10 '16 at 03:19