Reasoning about Locks and Transactions in Concurrent Programs