File indexing completed on 2024-12-08 04:27:18
0001 /* 0002 SPDX-FileCopyrightText: 2017 Nicolas Carion 0003 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 */ 0005 0006 #pragma once 0007 0008 /** This file contains a collection of macros that can be used in model related classes. 0009 The class only needs to have the following members: 0010 - For Push_undo : std::weak_ptr<DocUndoStack> m_undoStack; this is a pointer to the undoStack 0011 - For Update_undo_redo: mutable QReadWriteLock m_lock; This is a lock that ensures safety in case of concurrent access. Note that the mutex must be 0012 recursive. 0013 0014 See for example TimelineModel. 0015 0016 Note that there also exists a version of update_undo_redo without the need for a lock (but prefer the mutex version where applicable) 0017 */ 0018 0019 /** This convenience macro adds lock/unlock ability to a given lambda function 0020 Note that it is automatically called when you push the lambda so you shouldn't have 0021 to call it directly yourself 0022 */ 0023 #define LOCK_IN_LAMBDA(lambda) \ 0024 lambda = [this, lambda]() { \ 0025 m_lock.lockForWrite(); \ 0026 bool res_lambda = lambda(); \ 0027 m_lock.unlock(); \ 0028 return res_lambda; \ 0029 }; 0030 0031 /** This convenience macro locks the mutex for reading. 0032 Note that it might happen that a thread is executing a write operation that requires 0033 reading a Read-protected property. In that case, we try to write lock it first (this will be granted since the lock is recursive) 0034 */ 0035 #define READ_LOCK() \ 0036 std::unique_ptr<QReadLocker> rlocker(new QReadLocker(nullptr)); \ 0037 std::unique_ptr<QWriteLocker> wlocker(new QWriteLocker(nullptr)); \ 0038 if (m_lock.tryLockForWrite()) { \ 0039 /*we yield ownership of the lock to the WriteLocker*/ \ 0040 m_lock.unlock(); \ 0041 wlocker.reset(new QWriteLocker(&m_lock)); \ 0042 } else { \ 0043 rlocker.reset(new QReadLocker(&m_lock)); \ 0044 } 0045 0046 /** @brief This macro takes some lambdas that represent undo/redo for an operation and the text (name) associated with this operation 0047 * The lambdas are transformed to make sure they lock access to the class they operate on. 0048 * Then they are added on the undoStack 0049 */ 0050 #define PUSH_UNDO(undo, redo, text) \ 0051 if (auto ptr = m_undoStack.lock()) { \ 0052 ptr->push(new FunctionalUndoCommand(undo, redo, text)); \ 0053 } else { \ 0054 qDebug() << "ERROR : unable to access undo stack"; \ 0055 Q_ASSERT(false); \ 0056 } 0057 0058 /** @brief This macro takes as parameter one atomic operation and its reverse, and update 0059 * the undo and redo functional stacks/queue accordingly 0060 * This should be used in the rare case where we don't need a lock mutex. In general, prefer the other version 0061 */ 0062 #define UPDATE_UNDO_REDO_NOLOCK(operation, reverse, undo, redo) \ 0063 undo = [reverse, undo]() { \ 0064 bool v = reverse(); \ 0065 return undo() && v; \ 0066 }; \ 0067 redo = [operation, redo]() { \ 0068 bool v = redo(); \ 0069 return operation() && v; \ 0070 }; 0071 /** @brief This macro takes as parameter one atomic operation and its reverse, and update 0072 * the undo and redo functional stacks/queue accordingly 0073 * It will also ensure that operation and reverse are dealing with mutexes 0074 */ 0075 #define UPDATE_UNDO_REDO(operation, reverse, undo, redo) \ 0076 LOCK_IN_LAMBDA(operation) \ 0077 LOCK_IN_LAMBDA(reverse) \ 0078 UPDATE_UNDO_REDO_NOLOCK(operation, reverse, undo, redo)