Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tile_hash_table2.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2018 Andrey Kamakin <a.kamakin@icloud.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#ifndef KIS_TILEHASHTABLE_2_H
8#define KIS_TILEHASHTABLE_2_H
9
10#include "kis_shared.h"
11#include "kis_shared_ptr.h"
13#include "kis_tile.h"
14#include "kis_debug.h"
15
16#define SANITY_CHECK
17
31template <class T>
33
34template <class T>
36{
37 static constexpr bool isInherited = std::is_convertible<T*, KisShared*>::value;
38 Q_STATIC_ASSERT_X(isInherited, "Template must inherit KisShared");
39
40public:
41 typedef T TileType;
44
48
49 bool isEmpty()
50 {
51 return !m_numTiles.loadRelaxed();
52 }
53
54 bool tileExists(qint32 col, qint32 row);
55
62 TileTypeSP getExistingTile(qint32 col, qint32 row);
63
72 TileTypeSP getTileLazy(qint32 col, qint32 row, bool& newTile);
73
84 TileTypeSP getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile);
85 void addTile(TileTypeSP tile);
86 bool deleteTile(TileTypeSP tile);
87 bool deleteTile(qint32 col, qint32 row);
88
89 void clear();
90
93
100
101
102 qint32 numTiles()
103 {
104 return m_numTiles.loadRelaxed();
105 }
106
107 void debugPrintInfo();
108 void debugMaxListLength(qint32 &min, qint32 &max);
109
110 friend class KisTileHashTableIteratorTraits2<T>;
111
112private:
114 MemoryReclaimer(TileType *data) : d(data) {}
115
116 void destroy()
117 {
118 TileTypeSP::deref(reinterpret_cast<TileTypeSP*>(this), d);
119 delete this;
120 }
121
122 private:
124 };
125
126 inline quint32 calculateHashImpl(qint32 col, qint32 row)
127 {
128 if (col == 0 && row == 0) {
129 col = 0x7FFF;
130 row = 0x7FFF;
131 }
132
133 return ((static_cast<quint32>(row) << 16) | (static_cast<quint32>(col) & 0xFFFF));
134 }
135
136 inline quint32 calculateHash(qint32 col, qint32 row)
137 {
138#ifdef SANITY_CHECK
139 KIS_ASSERT_RECOVER_NOOP(qAbs(row) < 0x7FFF && qAbs(col) < 0x7FFF);
140#endif // SANITY_CHECK
141
142 return calculateHashImpl(col, row);
143 }
144
149 inline quint32 calculateHashSafe(qint32 col, qint32 row)
150 {
151 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(qAbs(row) < 0x7FFF && qAbs(col) < 0x7FFF, 0);
152 return calculateHashImpl(col, row);
153 }
154
155 inline void insert(quint32 idx, TileTypeSP item)
156 {
157 TileTypeSP::ref(&item, item.data());
158 TileType *tile = 0;
159
160 {
161 QReadLocker locker(&m_iteratorLock);
163 tile = m_map.assign(idx, item.data());
164 }
165
166 if (tile) {
167 tile->notifyDeadWithoutDetaching();
169 } else {
170 m_numTiles.fetchAndAddRelaxed(1);
171 }
172
174
175 m_map.getGC().update();
176 }
177
178 inline bool erase(quint32 idx)
179 {
181
182 bool wasDeleted = false;
183 TileType *tile = m_map.erase(idx);
184
185 if (tile) {
186 tile->notifyDetachedFromDataManager();
187
188 wasDeleted = true;
189 m_numTiles.fetchAndSubRelaxed(1);
191 }
192
194
195 m_map.getGC().update();
196 return wasDeleted;
197 }
198
199private:
201 typedef typename LockFreeTileMap::Mutator LockFreeTileMapMutator;
203
209 mutable QReadWriteLock m_iteratorLock;
210
211 QAtomicInt m_numTiles;
214};
215
216template <class T>
218{
219public:
220 typedef T TileType;
223
225 {
226 m_ht->m_iteratorLock.lockForWrite();
227 m_iter.setMap(m_ht->m_map);
228 }
229
231 {
232 m_ht->m_iteratorLock.unlock();
233 }
234
235 void next()
236 {
237 m_iter.next();
238 }
239
241 {
242 return TileTypeSP(m_iter.getValue());
243 }
244
245 bool isDone() const
246 {
247 return !m_iter.isValid();
248 }
249
251 {
252 m_ht->erase(m_iter.getKey());
253 next();
254 }
255
257 {
258 TileTypeSP tile = m_iter.getValue();
259 next();
260
261 quint32 idx = m_ht->calculateHash(tile->col(), tile->row());
262 m_ht->erase(idx);
263 newHashTable->insert(idx, tile);
264 }
265
266private:
269};
270
271template <class T>
273 : m_numTiles(0), m_defaultTileData(0), m_mementoManager(mm)
274{
275}
276
277template <class T>
280{
282
283 QWriteLocker locker(&ht.m_iteratorLock);
285
286 while (iter.isValid()) {
287 TileTypeSP tile = new TileType(*iter.getValue(), m_mementoManager);
288 insert(iter.getKey(), tile);
289 iter.next();
290 }
291}
292
293template <class T>
295{
296 clear();
297 setDefaultTileData(0);
298}
299
300template<class T>
301bool KisTileHashTableTraits2<T>::tileExists(qint32 col, qint32 row)
302{
303 return getExistingTile(col, row);
304}
305
306template <class T>
308{
309 const quint32 idx = calculateHashSafe(col, row);
310 if (!idx) {
312 return TileTypeSP();
313 }
314
315 m_map.getGC().lockRawPointerAccess();
316 TileTypeSP tile = m_map.get(idx);
317 m_map.getGC().unlockRawPointerAccess();
318
319 m_map.getGC().update();
320 return tile;
321}
322
323template <class T>
325{
326 newTile = false;
327 const quint32 idx = calculateHashSafe(col, row);
328 if (!idx) {
331
335 newTile = false;
336
337 QReadLocker locker(&m_defaultPixelDataLock);
338 return new TileType(col, row, m_defaultTileData, 0);
339 }
340
341 // we are going to assign a raw-pointer tile from the table
342 // to a shared pointer...
343 m_map.getGC().lockRawPointerAccess();
344
345 TileTypeSP tile = m_map.get(idx);
346
347 while (!tile) {
348 // we shouldn't try to acquire **any** lock with
349 // raw-pointer lock held
350 m_map.getGC().unlockRawPointerAccess();
351
352 {
353 QReadLocker locker(&m_defaultPixelDataLock);
354 tile = new TileType(col, row, m_defaultTileData, 0);
355 }
356
357 TileTypeSP::ref(&tile, tile.data());
358 TileType *discardedTile = 0;
359
360 // iterator lock should be taken **before**
361 // the pointers are locked
362 m_iteratorLock.lockForRead();
363
364 // and now lock raw-pointers again
365 m_map.getGC().lockRawPointerAccess();
366
367 // mutator might have become invalidated when
368 // we released raw pointers, so we need to reinitialize it
369 LockFreeTileMapMutator mutator = m_map.insertOrFind(idx);
370 if (!mutator.getValue()) {
371 discardedTile = mutator.exchangeValue(tile.data());
372 } else {
373 discardedTile = tile.data();
374 }
375
376 m_iteratorLock.unlock();
377
378 if (discardedTile) {
379 // we've got our tile back, it didn't manage to
380 // get into the table. Now release the allocated
381 // tile and push TO/GA switch.
382 tile = 0;
383
384 discardedTile->notifyDeadWithoutDetaching();
385 m_map.getGC().enqueue(&MemoryReclaimer::destroy, new MemoryReclaimer(discardedTile));
386
387 tile = m_map.get(idx);
388 continue;
389
390 } else {
391 newTile = true;
392 m_numTiles.fetchAndAddRelaxed(1);
393
394 tile->notifyAttachedToDataManager(m_mementoManager);
395 }
396 }
397 m_map.getGC().unlockRawPointerAccess();
398
399 m_map.getGC().update();
400 return tile;
401}
402
403template <class T>
405{
406 const quint32 idx = calculateHashSafe(col, row);
407 if (!idx) {
410
415 existingTile = false;
416
417 QReadLocker locker(&m_defaultPixelDataLock);
418 return new TileType(col, row, m_defaultTileData, 0);
419 }
420
421 m_map.getGC().lockRawPointerAccess();
422 TileTypeSP tile = m_map.get(idx);
423 m_map.getGC().unlockRawPointerAccess();
424
425 existingTile = tile;
426
427 if (!existingTile) {
428 QReadLocker locker(&m_defaultPixelDataLock);
429 tile = new TileType(col, row, m_defaultTileData, 0);
430 }
431
432 m_map.getGC().update();
433 return tile;
434}
435
436template <class T>
438{
439 quint32 idx = calculateHash(tile->col(), tile->row());
440 insert(idx, tile);
441}
442
443template <class T>
445{
446 return deleteTile(tile->col(), tile->row());
447}
448
449template <class T>
450bool KisTileHashTableTraits2<T>::deleteTile(qint32 col, qint32 row)
451{
452 const quint32 idx = calculateHashSafe(col, row);
453 if (!idx) {
455 return false;
456 }
457
458 return erase(idx);
459}
460
461template<class T>
463{
464 {
465 QWriteLocker locker(&m_iteratorLock);
466
468 TileType *tile = 0;
469
470 while (iter.isValid()) {
471 m_map.getGC().lockRawPointerAccess();
472 tile = m_map.erase(iter.getKey());
473
474 if (tile) {
475 tile->notifyDetachedFromDataManager();
476 m_map.getGC().enqueue(&MemoryReclaimer::destroy, new MemoryReclaimer(tile));
477 }
478 m_map.getGC().unlockRawPointerAccess();
479
480 iter.next();
481 }
482
483 m_numTiles.storeRelaxed(0);
484 }
485
486 // garbage collection must **not** be run with locks held
487 m_map.getGC().update();
488}
489
490template <class T>
492{
493 QWriteLocker locker(&m_defaultPixelDataLock);
494
495 if (m_defaultTileData) {
496 m_defaultTileData->release();
497 m_defaultTileData = 0;
498 }
499
500 if (defaultTileData) {
501 defaultTileData->acquire();
502 m_defaultTileData = defaultTileData;
503 }
504}
505
506template <class T>
508{
509 QReadLocker locker(&m_defaultPixelDataLock);
510 return m_defaultTileData;
511}
512
513template <class T>
515{
516 QReadLocker locker(&m_defaultPixelDataLock);
517 m_defaultTileData->ref();
518 return m_defaultTileData;
519}
520
521
522template <class T>
526
527template <class T>
528void KisTileHashTableTraits2<T>::debugMaxListLength(qint32 &/*min*/, qint32 &/*max*/)
529{
530}
531
535
536#endif // KIS_TILEHASHTABLE_2_H
Value erase(Key key)
Value assign(Key key, Value desired)
void deref() const
void ref() const
KisTileHashTableTraits2< T > * m_ht
void moveCurrentToHashTable(KisTileHashTableTraits2< T > *newHashTable)
ConcurrentMap< quint32, TileType * >::Iterator Iterator
KisTileHashTableIteratorTraits2(KisTileHashTableTraits2< T > *ht)
KisTileData * refAndFetchDefaultTileData()
ConcurrentMap< quint32, TileType * > LockFreeTileMap
TileTypeSP getExistingTile(qint32 col, qint32 row)
TileTypeSP getTileLazy(qint32 col, qint32 row, bool &newTile)
void insert(quint32 idx, TileTypeSP item)
static constexpr bool isInherited
quint32 calculateHashImpl(qint32 col, qint32 row)
KisWeakSharedPtr< T > TileTypeWSP
KisMementoManager * m_mementoManager
Q_STATIC_ASSERT_X(isInherited, "Template must inherit KisShared")
KisTileHashTableTraits2(KisMementoManager *mm)
quint32 calculateHash(qint32 col, qint32 row)
LockFreeTileMap::Mutator LockFreeTileMapMutator
quint32 calculateHashSafe(qint32 col, qint32 row)
TileTypeSP getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile)
void setDefaultTileData(KisTileData *defaultTileData)
void debugMaxListLength(qint32 &min, qint32 &max)
bool tileExists(qint32 col, qint32 row)
void addTile(TileTypeSP tile)
bool deleteTile(TileTypeSP tile)
void lockRawPointerAccess()
Definition qsbr.h:105
void enqueue(void(T::*pmf)(), T *target, bool migration=false)
Definition qsbr.h:71
void unlockRawPointerAccess()
Definition qsbr.h:110
void update()
Definition qsbr.h:93
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
KisTileHashTableIteratorTraits2< KisTile > KisTileHashTableConstIterator
KisTileHashTableIteratorTraits2< KisTile > KisTileHashTableIterator
KisTileHashTableTraits2< KisTile > KisTileHashTable
struct Tile * newTile(struct rect r)
Definition pixels.c:231