1 // Aseprite 2 // Copyright (C) 2001-2018 David Capello 3 // 4 // This program is distributed under the terms of 5 // the End-User License Agreement for Aseprite. 6 7 #ifndef APP_DOC_ACCESS_H_INCLUDED 8 #define APP_DOC_ACCESS_H_INCLUDED 9 #pragma once 10 11 #include "app/context.h" 12 #include "app/doc.h" 13 #include "base/exception.h" 14 15 #include <exception> 16 17 namespace app { 18 19 // TODO remove exceptions and use "DocAccess::operator bool()" 20 class LockedDocException : public base::Exception { 21 public: LockedDocException(const char * msg)22 LockedDocException(const char* msg) throw() 23 : base::Exception(msg) { } 24 }; 25 26 class CannotReadDocException : public LockedDocException { 27 public: CannotReadDocException()28 CannotReadDocException() throw() 29 : LockedDocException("Cannot read the sprite.\n" 30 "It is being modified by another command.\n" 31 "Try again.") { } 32 }; 33 34 class CannotWriteDocException : public LockedDocException { 35 public: CannotWriteDocException()36 CannotWriteDocException() throw() 37 : LockedDocException("Cannot modify the sprite.\n" 38 "It is being used by another command.\n" 39 "Try again.") { } 40 }; 41 42 // This class acts like a wrapper for the given document. It's 43 // specialized by DocReader/Writer to handle document read/write 44 // locks. 45 class DocAccess { 46 public: DocAccess()47 DocAccess() : m_doc(NULL) { } DocAccess(const DocAccess & copy)48 DocAccess(const DocAccess& copy) : m_doc(copy.m_doc) { } DocAccess(Doc * doc)49 explicit DocAccess(Doc* doc) : m_doc(doc) { } ~DocAccess()50 ~DocAccess() { } 51 52 DocAccess& operator=(const DocAccess& copy) { 53 m_doc = copy.m_doc; 54 return *this; 55 } 56 57 operator Doc*() { return m_doc; } 58 operator const Doc*() const { return m_doc; } 59 60 Doc* operator->() { 61 ASSERT(m_doc); 62 return m_doc; 63 } 64 65 const Doc* operator->() const { 66 ASSERT(m_doc); 67 return m_doc; 68 } 69 70 protected: 71 Doc* m_doc; 72 }; 73 74 // Class to view the document's state. Its constructor request a 75 // reader-lock of the document, or throws an exception in case that 76 // the lock cannot be obtained. 77 class DocReader : public DocAccess { 78 public: DocReader()79 DocReader() { 80 } 81 DocReader(Doc * doc,int timeout)82 explicit DocReader(Doc* doc, int timeout) 83 : DocAccess(doc) { 84 if (m_doc && !m_doc->lock(Doc::ReadLock, timeout)) 85 throw CannotReadDocException(); 86 } 87 DocReader(const DocReader & copy,int timeout)88 explicit DocReader(const DocReader& copy, int timeout) 89 : DocAccess(copy) { 90 if (m_doc && !m_doc->lock(Doc::ReadLock, timeout)) 91 throw CannotReadDocException(); 92 } 93 ~DocReader()94 ~DocReader() { 95 unlock(); 96 } 97 98 protected: unlock()99 void unlock() { 100 if (m_doc) { 101 m_doc->unlock(); 102 m_doc = nullptr; 103 } 104 } 105 106 private: 107 // Disable operator= 108 DocReader& operator=(const DocReader&); 109 }; 110 111 // Class to modify the document's state. Its constructor request a 112 // writer-lock of the document, or throws an exception in case that 113 // the lock cannot be obtained. Also, it contains a special 114 // constructor that receives a DocReader, to elevate the 115 // reader-lock to writer-lock. 116 class DocWriter : public DocAccess { 117 public: DocWriter()118 DocWriter() 119 : m_from_reader(false) 120 , m_locked(false) { 121 } 122 DocWriter(Doc * doc,int timeout)123 explicit DocWriter(Doc* doc, int timeout) 124 : DocAccess(doc) 125 , m_from_reader(false) 126 , m_locked(false) { 127 if (m_doc) { 128 if (!m_doc->lock(Doc::WriteLock, timeout)) 129 throw CannotWriteDocException(); 130 131 m_locked = true; 132 } 133 } 134 135 // Constructor that can be used to elevate the given reader-lock to 136 // writer permission. DocWriter(const DocReader & doc,int timeout)137 explicit DocWriter(const DocReader& doc, int timeout) 138 : DocAccess(doc) 139 , m_from_reader(true) 140 , m_locked(false) { 141 if (m_doc) { 142 if (!m_doc->upgradeToWrite(timeout)) 143 throw CannotWriteDocException(); 144 145 m_locked = true; 146 } 147 } 148 ~DocWriter()149 ~DocWriter() { 150 unlock(); 151 } 152 153 protected: unlock()154 void unlock() { 155 if (m_doc && m_locked) { 156 if (m_from_reader) 157 m_doc->downgradeToRead(); 158 else 159 m_doc->unlock(); 160 161 m_doc = nullptr; 162 m_locked = false; 163 } 164 } 165 166 private: 167 bool m_from_reader; 168 bool m_locked; 169 170 // Non-copyable 171 DocWriter(const DocWriter&); 172 DocWriter& operator=(const DocWriter&); 173 DocWriter& operator=(const DocReader&); 174 }; 175 176 // Used to destroy the active document in the context. 177 class DocDestroyer : public DocWriter { 178 public: DocDestroyer(Context * context,Doc * doc,int timeout)179 explicit DocDestroyer(Context* context, Doc* doc, int timeout) 180 : DocWriter(doc, timeout) { 181 } 182 destroyDocument()183 void destroyDocument() { 184 ASSERT(m_doc != NULL); 185 186 m_doc->close(); 187 Doc* doc = m_doc; 188 unlock(); 189 190 delete doc; 191 m_doc = nullptr; 192 } 193 194 }; 195 196 class WeakDocReader : public DocAccess { 197 public: WeakDocReader()198 WeakDocReader() { 199 } 200 WeakDocReader(Doc * doc)201 explicit WeakDocReader(Doc* doc) 202 : DocAccess(doc) 203 , m_weak_lock(base::RWLock::WeakUnlocked) { 204 if (m_doc) 205 m_doc->weakLock(&m_weak_lock); 206 } 207 ~WeakDocReader()208 ~WeakDocReader() { 209 weakUnlock(); 210 } 211 isLocked()212 bool isLocked() const { 213 return (m_weak_lock == base::RWLock::WeakLocked); 214 } 215 216 protected: weakUnlock()217 void weakUnlock() { 218 if (m_doc && m_weak_lock != base::RWLock::WeakUnlocked) { 219 m_doc->weakUnlock(); 220 m_doc = nullptr; 221 } 222 } 223 224 private: 225 // Disable operator= 226 WeakDocReader(const WeakDocReader&); 227 WeakDocReader& operator=(const WeakDocReader&); 228 229 base::RWLock::WeakLock m_weak_lock; 230 }; 231 232 } // namespace app 233 234 #endif 235