• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #4473

08 Jul 2025 09:38AM UTC coverage: 62.922% (+0.7%) from 62.22%
#4473

push

travis-ci

web-flow
Merge pull request #31712 from taosdata/merge/mainto3.0

merge: from main to 3.0 branch

158525 of 321496 branches covered (49.31%)

Branch coverage included in aggregate %.

56 of 60 new or added lines in 13 files covered. (93.33%)

1333 existing lines in 67 files now uncovered.

245526 of 320647 relevant lines covered (76.57%)

17689640.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.89
/source/util/src/tcache.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15

16
#define _DEFAULT_SOURCE
17
#include "tcache.h"
18
#include "osThread.h"
19
#include "taoserror.h"
20
#include "tlog.h"
21
#include "tutil.h"
22

23
#define CACHE_MAX_CAPACITY     1024 * 1024 * 16
24
#define CACHE_DEFAULT_CAPACITY 1024 * 4
25

26
static TdThread      cacheRefreshWorker = {0};
27
static TdThreadOnce  cacheThreadInit = PTHREAD_ONCE_INIT;
28
static TdThreadMutex guard;
29
static SArray       *pCacheArrayList = NULL;
30
static bool          stopRefreshWorker = false;
31
static bool          refreshWorkerNormalStopped = false;
32
static bool          refreshWorkerUnexpectedStopped = false;
33

34
typedef struct SCacheNode {
35
  uint64_t           addedTime;   // the added time when this element is added or updated into cache
36
  uint64_t           lifespan;    // life duration when this element should be remove from cache
37
  int64_t            expireTime;  // expire time
38
  void              *signature;
39
  struct STrashElem *pTNodeHeader;    // point to trash node head
40
  uint16_t           keyLen : 15;     // max key size: 32kb
41
  bool               inTrashcan : 1;  // denote if it is in trash or not
42
  uint32_t           size;            // allocated size for current SCacheNode
43
  uint32_t           dataLen;
44
  T_REF_DECLARE()
45
  struct SCacheNode *pNext;
46
  char              *key;
47
  char              *data;
48
} SCacheNode;
49

50
typedef struct SCacheEntry {
51
  int32_t     num;    // number of elements in current entry
52
  SRWLatch    latch;  // entry latch
53
  SCacheNode *next;
54
} SCacheEntry;
55

56
struct STrashElem {
57
  struct STrashElem *prev;
58
  struct STrashElem *next;
59
  SCacheNode        *pData;
60
};
61

62
struct SCacheIter {
63
  SCacheObj   *pCacheObj;
64
  SCacheNode **pCurrent;
65
  int32_t      entryIndex;
66
  int32_t      index;
67
  int32_t      numOfObj;
68
};
69

70
/*
71
 * to accommodate the old data which has the same key value of new one in hashList
72
 * when an new node is put into cache, if an existed one with the same key:
73
 * 1. if the old one does not be referenced, update it.
74
 * 2. otherwise, move the old one to pTrash, addedTime the new one.
75
 *
76
 * when the node in pTrash does not be referenced, it will be release at the expired expiredTime
77
 */
78
struct SCacheObj {
79
  int64_t      sizeInBytes;  // total allocated buffer in this hash table, SCacheObj is not included.
80
  int64_t      refreshTime;
81
  char        *name;
82
  SCacheStatis statistics;
83

84
  SCacheEntry      *pEntryList;
85
  size_t            capacity;    // number of slots
86
  size_t            numOfElems;  // number of elements in cache
87
  _hash_fn_t        hashFp;      // hash function
88
  __cache_free_fn_t freeFp;
89

90
  uint32_t    numOfElemsInTrash;  // number of element in trash
91
  STrashElem *pTrash;
92

93
  uint8_t  deleting;  // set the deleting flag to stop refreshing ASAP.
94
  TdThread refreshWorker;
95
  bool     extendLifespan;  // auto extend life span when one item is accessed.
96
  int64_t  checkTick;       // tick used to record the check times of the refresh threads
97
#if defined(LINUX)
98
  TdThreadRwlock lock;
99
#else
100
  TdThreadMutex lock;
101
#endif
102
};
103

104
typedef struct SCacheObjTravSup {
105
  SCacheObj        *pCacheObj;
106
  int64_t           time;
107
  __cache_trav_fn_t fp;
108
  void             *param1;
109
} SCacheObjTravSup;
110

111
static FORCE_INLINE void __trashcan_wr_lock(SCacheObj *pCacheObj) {
112
#if defined(LINUX)
113
  (void)taosThreadRwlockWrlock(&pCacheObj->lock);
13✔
114
#else
115
  (void)taosThreadMutexLock(&pCacheObj->lock);
116
#endif
117
}
62,537✔
118

119
static FORCE_INLINE void __trashcan_unlock(SCacheObj *pCacheObj) {
120
#if defined(LINUX)
121
  (void)taosThreadRwlockUnlock(&pCacheObj->lock);
62,524✔
122
#else
123
  (void)taosThreadMutexUnlock(&pCacheObj->lock);
124
#endif
125
}
62,537✔
126

127
static FORCE_INLINE int32_t __trashcan_lock_init(SCacheObj *pCacheObj) {
128
#if defined(LINUX)
129
  return taosThreadRwlockInit(&pCacheObj->lock, NULL);
7,482✔
130
#else
131
  return taosThreadMutexInit(&pCacheObj->lock, NULL);
132
#endif
133
}
134

135
static FORCE_INLINE void __trashcan_lock_destroy(SCacheObj *pCacheObj) {
136
#if defined(LINUX)
137
  (void)taosThreadRwlockDestroy(&pCacheObj->lock);
7,479✔
138
#else
139
  (void)taosThreadMutexDestroy(&pCacheObj->lock);
140
#endif
141
}
7,479✔
142

143
/**
144
 * do cleanup the taos cache
145
 * @param pCacheObj
146
 */
147
static void doCleanupDataCache(SCacheObj *pCacheObj);
148

149
/**
150
 * refresh cache to remove data in both hash list and trash, if any nodes' refcount == 0, every pCacheObj->refreshTime
151
 * @param handle   Cache object handle
152
 */
153
static void *taosCacheTimedRefresh(void *handle);
154

155
static void doInitRefreshThread(void) {
2,374✔
156
  pCacheArrayList = taosArrayInit(4, POINTER_BYTES);
2,374✔
157
  if (pCacheArrayList == NULL) {
2,374!
158
    uError("failed to allocate memory, reason:%s", strerror(ERRNO));
×
159
    return;
×
160
  }
161

162
  (void)taosThreadMutexInit(&guard, NULL);
2,374✔
163

164
  TdThreadAttr thattr;
165
  (void)taosThreadAttrInit(&thattr);
2,374✔
166
  (void)taosThreadAttrSetDetachState(&thattr, PTHREAD_CREATE_JOINABLE);
2,374✔
167

168
  (void)taosThreadCreate(&cacheRefreshWorker, &thattr, taosCacheTimedRefresh, NULL);
2,374✔
169
  (void)taosThreadAttrDestroy(&thattr);
2,374✔
170
}
171

172
TdThread doRegisterCacheObj(SCacheObj *pCacheObj) {
7,482✔
173
  (void)taosThreadOnce(&cacheThreadInit, doInitRefreshThread);
7,482✔
174

175
  (void)taosThreadMutexLock(&guard);
7,482✔
176
  if (taosArrayPush(pCacheArrayList, &pCacheObj) == NULL) {
14,964!
177
    uError("failed to add cache object into array, reason:%s", strerror(ERRNO));
×
178
    (void)taosThreadMutexUnlock(&guard);
×
179
    return cacheRefreshWorker;
×
180
  }
181
  (void)taosThreadMutexUnlock(&guard);
7,482✔
182

183
  return cacheRefreshWorker;
7,482✔
184
}
185

186
/**
187
 * @param key      key of object for hash, usually a null-terminated string
188
 * @param keyLen   length of key
189
 * @param pData    actually data. required a consecutive memory block, no pointer is allowed
190
 *                 in pData. Pointer copy causes memory access error.
191
 * @param size     size of block
192
 * @param lifespan total survial expiredTime from now
193
 * @return         SCacheNode
194
 */
195
static SCacheNode *taosCreateCacheNode(const char *key, size_t keyLen, const char *pData, size_t size,
196
                                       uint64_t duration);
197

198
/**
199
 * addedTime object node into trash, and this object is closed for referencing if it is addedTime to trash
200
 * It will be removed until the pNode->refCount == 0
201
 * @param pCacheObj    Cache object
202
 * @param pNode   Cache slot object
203
 */
204
static void taosAddToTrashcan(SCacheObj *pCacheObj, SCacheNode *pNode);
205

206
/**
207
 * remove nodes in trash with refCount == 0 in cache
208
 * @param pNode
209
 * @param pCacheObj
210
 * @param force   force model, if true, remove data in trash without check refcount.
211
 *                may cause corruption. So, forece model only applys before cache is closed
212
 */
213
static void taosTrashcanEmpty(SCacheObj *pCacheObj, bool force);
214

215
/**
216
 * release node
217
 * @param pCacheObj      cache object
218
 * @param pNode          data node
219
 */
220
static FORCE_INLINE void taosCacheReleaseNode(SCacheObj *pCacheObj, SCacheNode *pNode) {
221
  if (pNode->signature != pNode) {
344,270!
222
    uError("key:%s, %p data is invalid, or has been released", pNode->key, pNode);
×
223
    return;
×
224
  }
225

226
  (void)atomic_sub_fetch_64(&pCacheObj->sizeInBytes, pNode->size);
344,270✔
227

228
  uDebug("cache:%s, key:%p, %p is destroyed from cache, size:%dbytes, total num:%d size:%" PRId64 "bytes",
344,270✔
229
         pCacheObj->name, pNode->key, pNode->data, pNode->size, (int)pCacheObj->numOfElems - 1, pCacheObj->sizeInBytes);
230

231
  if (pCacheObj->freeFp) {
344,270!
232
    pCacheObj->freeFp(pNode->data);
344,270✔
233
  }
234

235
  taosMemoryFree(pNode);
344,270!
236
}
237

238
static FORCE_INLINE STrashElem *doRemoveElemInTrashcan(SCacheObj *pCacheObj, STrashElem *pElem) {
239
  if (pElem->pData->signature != pElem->pData) {
13!
240
    uWarn("key:sig:0x%" PRIx64 " %p data has been released, ignore", (int64_t)pElem->pData->signature, pElem->pData);
×
241
    terrno = TSDB_CODE_INVALID_PARA;
×
242
    return NULL;
×
243
  }
244

245
  STrashElem *next = pElem->next;
13✔
246

247
  pCacheObj->numOfElemsInTrash--;
13✔
248
  if (pElem->prev) {
13!
249
    pElem->prev->next = pElem->next;
×
250
  } else {  // pnode is the header, update header
251
    pCacheObj->pTrash = pElem->next;
13✔
252
  }
253

254
  if (next) {
13!
255
    next->prev = pElem->prev;
3✔
256
  }
257
  return next;
13✔
258
}
259

260
static FORCE_INLINE void doDestroyTrashcanElem(SCacheObj *pCacheObj, STrashElem *pElem) {
261
  if (pCacheObj->freeFp) {
13!
262
    pCacheObj->freeFp(pElem->pData->data);
13✔
263
  }
264

265
  taosMemoryFree(pElem->pData);
13!
266
  taosMemoryFree(pElem);
13!
267
}
13✔
268

269
static void pushfrontNodeInEntryList(SCacheEntry *pEntry, SCacheNode *pNode) {
344,017✔
270
  pNode->pNext = pEntry->next;
344,017✔
271
  pEntry->next = pNode;
344,017✔
272
  pEntry->num += 1;
344,017✔
273
  // A S S E R T((pEntry->next && pEntry->num > 0) || (NULL == pEntry->next && pEntry->num == 0));
274
}
344,017✔
275

276
static void removeNodeInEntryList(SCacheEntry *pe, SCacheNode *prev, SCacheNode *pNode) {
12✔
277
  if (prev == NULL) {
12!
278
    pe->next = pNode->pNext;
12✔
279
  } else {
280
    prev->pNext = pNode->pNext;
×
281
  }
282

283
  pNode->pNext = NULL;
12✔
284
  pe->num -= 1;
12✔
285
  // A S S E R T((pe->next && pe->num > 0) || (NULL == pe->next && pe->num == 0));
286
}
12✔
287

288
static FORCE_INLINE SCacheEntry *doFindEntry(SCacheObj *pCacheObj, const void *key, size_t keyLen) {
289
  uint32_t hashVal = (*pCacheObj->hashFp)(key, keyLen);
344,233✔
290
  int32_t  slot = hashVal % pCacheObj->capacity;
1,028,788✔
291
  return &pCacheObj->pEntryList[slot];
1,028,788✔
292
}
293

294
static FORCE_INLINE SCacheNode *doSearchInEntryList(SCacheEntry *pe, const void *key, size_t keyLen,
295
                                                    SCacheNode **prev) {
296
  SCacheNode *pNode = pe->next;
1,028,810✔
297
  while (pNode) {
1,112,245!
298
    if ((pNode->keyLen == keyLen) && memcmp(pNode->key, key, keyLen) == 0) {
765,641!
299
      break;
682,206✔
300
    }
301
    *prev = pNode;
83,435✔
302
    pNode = pNode->pNext;
83,435✔
303
  }
304

305
  return pNode;
1,028,810✔
306
}
307

308
static bool doRemoveExpiredFn(void *param, SCacheNode *pNode) {
1,474,778✔
309
  SCacheObjTravSup *ps = (SCacheObjTravSup *)param;
1,474,778✔
310
  SCacheObj        *pCacheObj = ps->pCacheObj;
1,474,778✔
311

312
  if ((int64_t)pNode->expireTime < ps->time && T_REF_VAL_GET(pNode) <= 0) {
1,474,778✔
313
    taosCacheReleaseNode(pCacheObj, pNode);
314

315
    // this node should be remove from hash table
316
    return false;
324,762✔
317
  }
318

319
  if (ps->fp) {
1,150,016!
320
    (ps->fp)(pNode->data, ps->param1);
×
321
  }
322

323
  // do not remove element in hash table
324
  return true;
1,150,016✔
325
}
326

327
static bool doRemoveNodeFn(void *param, SCacheNode *pNode) {
19,512✔
328
  SCacheObjTravSup *ps = (SCacheObjTravSup *)param;
19,512✔
329
  SCacheObj        *pCacheObj = ps->pCacheObj;
19,512✔
330

331
  if (T_REF_VAL_GET(pNode) == 0) {
19,512✔
332
    taosCacheReleaseNode(pCacheObj, pNode);
333
  } else {  // do add to trashcan
334
    taosAddToTrashcan(pCacheObj, pNode);
4✔
335
  }
336

337
  // this node should be remove from hash table
338
  return false;
19,512✔
339
}
340

341
static FORCE_INLINE int32_t getCacheCapacity(int32_t length) {
342
  int32_t len = 0;
343
  if (length < CACHE_DEFAULT_CAPACITY) {
344
    len = CACHE_DEFAULT_CAPACITY;
345
    return len;
346
  } else if (length > CACHE_MAX_CAPACITY) {
347
    len = CACHE_MAX_CAPACITY;
348
    return len;
349
  }
350

351
  len = CACHE_DEFAULT_CAPACITY;
352
  while (len < length && len < CACHE_MAX_CAPACITY) {
353
    len = (len << 1u);
354
  }
355

356
  return len > CACHE_MAX_CAPACITY ? CACHE_MAX_CAPACITY : len;
357
}
358

359
SCacheObj *taosCacheInit(int32_t keyType, int64_t refreshTimeInMs, bool extendLifespan, __cache_free_fn_t fn,
7,482✔
360
                         const char *cacheName) {
361
  const int32_t SLEEP_DURATION = 500;  // 500 ms
7,482✔
362

363
  if (refreshTimeInMs <= 0) {
7,482!
364
    return NULL;
×
365
  }
366

367
  SCacheObj *pCacheObj = (SCacheObj *)taosMemoryCalloc(1, sizeof(SCacheObj));
7,482!
368
  if (pCacheObj == NULL) {
7,482!
369
    uError("failed to allocate memory, reason:%s", strerror(ERRNO));
×
370
    terrno = TSDB_CODE_OUT_OF_MEMORY;
×
371
    return NULL;
×
372
  }
373

374
  // TODO add the auto extend procedure
375
  pCacheObj->capacity = 4096;
7,482✔
376
  pCacheObj->pEntryList = taosMemoryCalloc(pCacheObj->capacity, sizeof(SCacheEntry));
7,482!
377
  if (pCacheObj->pEntryList == NULL) {
7,482!
378
    taosMemoryFree(pCacheObj);
×
379
    uError("failed to allocate memory, reason:%s", strerror(ERRNO));
×
380
    return NULL;
×
381
  }
382

383
  pCacheObj->name = taosStrdup(cacheName);
7,482!
384
  if (pCacheObj->name == NULL) {
7,482!
385
    taosMemoryFreeClear(pCacheObj->pEntryList);
×
386
    taosMemoryFree(pCacheObj);
×
387
    uError("failed to allocate memory, reason:%s", terrstr());
×
388
    return NULL;
×
389
  }
390

391
  // set free cache node callback function
392
  pCacheObj->hashFp = taosGetDefaultHashFunction(keyType);
7,482✔
393
  pCacheObj->freeFp = fn;
7,482✔
394
  pCacheObj->refreshTime = refreshTimeInMs;
7,482✔
395
  pCacheObj->checkTick = pCacheObj->refreshTime / SLEEP_DURATION;
7,482✔
396
  pCacheObj->extendLifespan = extendLifespan;  // the TTL after the last access
7,482✔
397

398
  if (__trashcan_lock_init(pCacheObj) != 0) {
7,482!
399
    taosMemoryFreeClear(pCacheObj->pEntryList);
×
400
    taosMemoryFreeClear(pCacheObj->name);
×
401
    taosMemoryFree(pCacheObj);
×
402

403
    uError("failed to init lock, reason:%s", strerror(ERRNO));
×
404
    return NULL;
×
405
  }
406

407
  TdThread refreshWorker = doRegisterCacheObj(pCacheObj);
7,482✔
408
  return pCacheObj;
7,482✔
409
}
410

411
void *taosCachePut(SCacheObj *pCacheObj, const void *key, size_t keyLen, const void *pData, size_t dataSize,
343,974✔
412
                   int32_t durationMS) {
413
  if (pCacheObj == NULL || pCacheObj->pEntryList == NULL || pCacheObj->deleting == 1) {
343,974!
414
    terrno = TSDB_CODE_INVALID_PARA;
83✔
415
    return NULL;
×
416
  }
417

418
  SCacheNode *pNode1 = taosCreateCacheNode(key, keyLen, pData, dataSize, durationMS);
343,891✔
419
  if (pNode1 == NULL) {
344,185!
420
    uError("cache:%s, key:%p, failed to added into cache, out of memory", pCacheObj->name, key);
×
421
    return NULL;
×
422
  }
423

424
  T_REF_INC(pNode1);
344,185✔
425

426
  SCacheEntry *pe = doFindEntry(pCacheObj, key, keyLen);
344,100✔
427

428
  taosWLockLatch(&pe->latch);
344,100✔
429

430
  SCacheNode *prev = NULL;
344,094✔
431
  SCacheNode *pNode = doSearchInEntryList(pe, key, keyLen, &prev);
344,094✔
432

433
  if (pNode == NULL) {
344,094!
434
    pushfrontNodeInEntryList(pe, pNode1);
344,098✔
435
    (void)atomic_add_fetch_ptr(&pCacheObj->numOfElems, 1);
344,028✔
436
    (void)atomic_add_fetch_ptr(&pCacheObj->sizeInBytes, pNode1->size);
344,180✔
437
    uDebug("cache:%s, key:%p, %p added into cache, added:%" PRIu64 ", expire:%" PRIu64
344,143✔
438
           ", totalNum:%d sizeInBytes:%" PRId64 "bytes size:%" PRId64 "bytes",
439
           pCacheObj->name, key, pNode1->data, pNode1->addedTime, pNode1->expireTime, (int32_t)pCacheObj->numOfElems,
440
           pCacheObj->sizeInBytes, (int64_t)dataSize);
441
  } else {  // duplicated key exists
442
    // move current node to trashcan
UNCOV
443
    removeNodeInEntryList(pe, prev, pNode);
×
444

445
    if (T_REF_VAL_GET(pNode) == 0) {
12✔
446
      if (pCacheObj->freeFp) {
3!
447
        pCacheObj->freeFp(pNode->data);
3✔
448
      }
449

450
      (void)atomic_sub_fetch_64(&pCacheObj->sizeInBytes, pNode->size);
3✔
451
      taosMemoryFreeClear(pNode);
3!
452
    } else {
453
      taosAddToTrashcan(pCacheObj, pNode);
9✔
454
      uDebug("cache:%s, key:%p, %p exist in cache, updated old:%p", pCacheObj->name, key, pNode1->data, pNode->data);
9✔
455
    }
456

457
    pushfrontNodeInEntryList(pe, pNode1);
12✔
458
    (void)atomic_add_fetch_64(&pCacheObj->sizeInBytes, pNode1->size);
12✔
459
    uDebug("cache:%s, key:%p, %p added into cache, added:%" PRIu64 ", expire:%" PRIu64
12✔
460
           ", totalNum:%d sizeInBytes:%" PRId64 "bytes size:%" PRId64 "bytes",
461
           pCacheObj->name, key, pNode1->data, pNode1->addedTime, pNode1->expireTime, (int32_t)pCacheObj->numOfElems,
462
           pCacheObj->sizeInBytes, (int64_t)dataSize);
463
  }
464

465
  taosWUnLockLatch(&pe->latch);
344,155✔
466
  return pNode1->data;
344,126✔
467
}
468

469
void *taosCacheAcquireByKey(SCacheObj *pCacheObj, const void *key, size_t keyLen) {
687,547✔
470
  if (pCacheObj == NULL || pCacheObj->deleting == 1) {
687,547!
471
    return NULL;
4✔
472
  }
473

474
  if (pCacheObj->numOfElems == 0) {
687,543✔
475
    (void)atomic_add_fetch_64(&pCacheObj->statistics.missCount, 1);
2,853✔
476
    return NULL;
2,853✔
477
  }
478

479
  SCacheNode  *prev = NULL;
684,690✔
480
  SCacheEntry *pe = doFindEntry(pCacheObj, key, keyLen);
684,688✔
481

482
  taosRLockLatch(&pe->latch);
684,688✔
483

484
  SCacheNode *pNode = doSearchInEntryList(pe, key, keyLen, &prev);
684,716✔
485
  if (pNode != NULL) {
684,716✔
486
    int32_t ref = T_REF_INC(pNode);
682,200✔
487
  }
488

489
  taosRUnLockLatch(&pe->latch);
684,717✔
490

491
  void *pData = (pNode != NULL) ? pNode->data : NULL;
684,713✔
492
  if (pData != NULL) {
684,713✔
493
    (void)atomic_add_fetch_64(&pCacheObj->statistics.hitCount, 1);
682,207✔
494
    uDebug("cache:%s, key:%p, %p is retrieved from cache, refcnt:%d", pCacheObj->name, key, pData,
682,201✔
495
           T_REF_VAL_GET(pNode));
496
  } else {
497
    (void)atomic_add_fetch_64(&pCacheObj->statistics.missCount, 1);
2,506✔
498
    uDebug("cache:%s, key:%p, not in cache, retrieved failed", pCacheObj->name, key);
2,514✔
499
  }
500

501
  (void)atomic_add_fetch_64(&pCacheObj->statistics.totalAccess, 1);
684,715✔
502
  return pData;
684,719✔
503
}
504

505
void *taosCacheAcquireByData(SCacheObj *pCacheObj, void *data) {
×
506
  if (pCacheObj == NULL || data == NULL) return NULL;
×
507

508
  SCacheNode *ptNode = (SCacheNode *)((char *)data - sizeof(SCacheNode));
×
509
  if (ptNode->signature != ptNode) {
×
510
    uError("cache:%s, key: %p the data from cache is invalid", pCacheObj->name, ptNode);
×
511
    return NULL;
×
512
  }
513

514
  int32_t ref = T_REF_INC(ptNode);
×
515
  uDebug("cache:%s, data: %p acquired by data in cache, refcnt:%d", pCacheObj->name, ptNode->data, ref);
×
516

517
  // the data if referenced by at least one object, so the reference count must be greater than the value of 2.
518
  // A S S E R T(ref >= 2);
519
  return data;
×
520
}
521

522
void *taosCacheTransferData(SCacheObj *pCacheObj, void **data) {
×
523
  if (pCacheObj == NULL || data == NULL || (*data) == NULL) {
×
524
    terrno = TSDB_CODE_INVALID_PARA;
×
525
    return NULL;
×
526
  }
527

528
  SCacheNode *ptNode = (SCacheNode *)((char *)(*data) - sizeof(SCacheNode));
×
529
  if (ptNode->signature != ptNode) {
×
530
    uError("cache:%s, key: %p the data from cache is invalid", pCacheObj->name, ptNode);
×
531
    terrno = TSDB_CODE_INVALID_PARA;
×
532
    return NULL;
×
533
  }
534

535
  char *d = *data;
×
536

537
  // clear its reference to old area
538
  *data = NULL;
×
539
  return d;
×
540
}
541

542
void taosCacheRelease(SCacheObj *pCacheObj, void **data, bool _remove) {
2,217,269✔
543
  if (pCacheObj == NULL) {
2,217,269!
544
    return;
×
545
  }
546

547
  if ((*data) == NULL) {
2,217,269!
548
    uError("cache:%s, NULL data to release", pCacheObj->name);
×
549
    return;
×
550
  }
551

552
  // The operation of removal from hash table and addition to trashcan is not an atomic operation,
553
  // therefore the check for the empty of both the hash table and the trashcan has a race condition.
554
  // It happens when there is only one object in the cache, and two threads which has referenced this object
555
  // start to free the it simultaneously [TD-1569].
556
  SCacheNode *pNode = (SCacheNode *)((char *)(*data) - sizeof(SCacheNode));
2,217,269✔
557
  if (pNode->signature != pNode) {
2,217,269!
558
    uError("cache:%s, %p, release invalid cache data", pCacheObj->name, pNode);
×
559
    return;
×
560
  }
561

562
  *data = NULL;
2,217,269✔
563

564
  // note: extend lifespan before dec ref count
565
  bool inTrashcan = pNode->inTrashcan;
2,217,269✔
566

567
  if (pCacheObj->extendLifespan && (!inTrashcan) && (!_remove)) {
2,217,269!
568
    atomic_store_64(&pNode->expireTime, pNode->lifespan + taosGetTimestampMs());
1,595,719✔
569
    uDebug("cache:%s, data:%p extend expire time: %" PRId64, pCacheObj->name, pNode->data, pNode->expireTime);
798,507✔
570
  }
571

572
  if (_remove) {
2,218,213!
573
    // NOTE: once refcount is decrease, pNode may be freed by other thread immediately.
574
    char *key = pNode->key;
×
575
    char *d = pNode->data;
×
576

577
    int32_t ref = T_REF_VAL_GET(pNode);
×
578
    uDebug("cache:%s, key:%p, %p is released, refcnt:%d, in trashcan:%d", pCacheObj->name, key, d, ref - 1, inTrashcan);
×
579

580
    /*
581
     * If it is not referenced by other users, remove it immediately. Otherwise move this node to trashcan wait for all
582
     * users releasing this resources.
583
     *
584
     * NOTE: previous ref is 0, and current ref is still 0, remove it. If previous is not 0, there is another thread
585
     * that tries to do the same thing.
586
     */
587
    if (inTrashcan) {
×
588
      ref = T_REF_VAL_GET(pNode);
×
589

590
      if (ref == 1) {
×
591
        // If it is the last ref, remove it from trashcan linked-list first, and then destroy it.Otherwise, it may be
592
        // destroyed by refresh worker if decrease ref count before removing it from linked-list.
593
        // A S S E R T(pNode->pTNodeHeader->pData == pNode);
594

595
        __trashcan_wr_lock(pCacheObj);
596
        (void)doRemoveElemInTrashcan(pCacheObj, pNode->pTNodeHeader);
×
597
        __trashcan_unlock(pCacheObj);
598

599
        ref = T_REF_DEC(pNode);
×
600
        // A S S E R T(ref == 0);
601

602
        doDestroyTrashcanElem(pCacheObj, pNode->pTNodeHeader);
×
603
      } else {
604
        ref = T_REF_DEC(pNode);
×
605
        // A S S E R T(ref >= 0);
606
      }
607
    } else {
608
      // NOTE: remove it from hash in the first place, otherwise, the pNode may have been released by other thread
609
      // when reaches here.
610
      SCacheNode  *prev = NULL;
×
611
      SCacheEntry *pe = doFindEntry(pCacheObj, pNode->key, pNode->keyLen);
×
612

613
      taosWLockLatch(&pe->latch);
×
614
      ref = T_REF_DEC(pNode);
×
615

616
      SCacheNode *p = doSearchInEntryList(pe, pNode->key, pNode->keyLen, &prev);
×
617

618
      if (p != NULL) {
×
619
        // successfully remove from hash table, if failed, this node must have been move to trash already, do nothing.
620
        // note that the remove operation can be executed only once.
621
        if (p != pNode) {
×
622
          uDebug(
×
623
              "cache:%s, key:%p, a new entry:%p found, refcnt:%d, prev entry:%p, refcnt:%d has been removed by "
624
              "others already, prev must in trashcan",
625
              pCacheObj->name, pNode->key, p->data, T_REF_VAL_GET(p), pNode->data, T_REF_VAL_GET(pNode));
626

627
          // A S S E R T(p->pTNodeHeader == NULL && pNode->pTNodeHeader != NULL);
628
        } else {
629
          removeNodeInEntryList(pe, prev, p);
×
630
          uDebug("cache:%s, key:%p, %p successfully removed from hash table, refcnt:%d", pCacheObj->name, pNode->key,
×
631
                 pNode->data, ref);
632
          if (ref > 0) {
×
633
            taosAddToTrashcan(pCacheObj, pNode);
×
634
          } else {  // ref == 0
635
            (void)atomic_sub_fetch_64(&pCacheObj->sizeInBytes, pNode->size);
×
636

637
            int32_t size = (int32_t)pCacheObj->numOfElems;
×
638
            uDebug("cache:%s, key:%p, %p is destroyed from cache, size:%dbytes, totalNum:%d size:%" PRId64 "bytes",
×
639
                   pCacheObj->name, pNode->key, pNode->data, pNode->size, size, pCacheObj->sizeInBytes);
640

641
            if (pCacheObj->freeFp) {
×
642
              pCacheObj->freeFp(pNode->data);
×
643
            }
644

645
            taosMemoryFree(pNode);
×
646
          }
647
        }
648

649
        taosWUnLockLatch(&pe->latch);
×
650
      } else {
651
        uDebug("cache:%s, key:%p, %p has been removed from hash table by others already, refcnt:%d", pCacheObj->name,
×
652
               pNode->key, pNode->data, ref);
653
      }
654
    }
655

656
  } else {
657
    // NOTE: once refcount is decrease, pNode may be freed by other thread immediately.
658
    char *key = pNode->key;
2,218,213✔
659
    char *p = pNode->data;
2,218,213✔
660

661
    int32_t ref = T_REF_DEC(pNode);
2,218,213✔
662
    uDebug("cache:%s, key:%p, %p released, refcnt:%d, data in trashcan:%d", pCacheObj->name, key, p, ref, inTrashcan);
2,220,943✔
663
  }
664
}
665

666
void doTraverseElems(SCacheObj *pCacheObj, bool (*fp)(void *param, SCacheNode *pNode), SCacheObjTravSup *pSup) {
62,524✔
667
  int32_t numOfEntries = (int32_t)pCacheObj->capacity;
62,524✔
668
  for (int32_t i = 0; i < numOfEntries; ++i) {
256,160,828✔
669
    SCacheEntry *pEntry = &pCacheObj->pEntryList[i];
256,098,304✔
670
    if (pEntry->num == 0) {
256,098,304✔
671
      continue;
254,618,616✔
672
    }
673

674
    taosWLockLatch(&pEntry->latch);
1,479,688✔
675

676
    SCacheNode **pPre = &pEntry->next;
1,479,688✔
677
    SCacheNode  *pNode = pEntry->next;
1,479,688✔
678
    while (pNode != NULL) {
2,973,978✔
679
      SCacheNode *next = pNode->pNext;
1,494,290✔
680

681
      if (fp(pSup, pNode)) {
1,494,290✔
682
        pPre = &pNode->pNext;
1,150,016✔
683
        pNode = pNode->pNext;
1,150,016✔
684
      } else {
685
        *pPre = next;
344,274✔
686
        pEntry->num -= 1;
344,274✔
687
        // A S S E R T((pEntry->next && pEntry->num > 0) || (NULL == pEntry->next && pEntry->num == 0));
688

689
        (void)atomic_sub_fetch_ptr(&pCacheObj->numOfElems, 1);
344,274✔
690
        pNode = next;
344,274✔
691
      }
692
    }
693

694
    taosWUnLockLatch(&pEntry->latch);
1,479,688✔
695
  }
696
}
62,524✔
697

698
void taosCacheEmpty(SCacheObj *pCacheObj) {
×
699
  SCacheObjTravSup sup = {.pCacheObj = pCacheObj, .fp = NULL, .time = taosGetTimestampMs()};
×
700
  doTraverseElems(pCacheObj, doRemoveNodeFn, &sup);
×
701
  taosTrashcanEmpty(pCacheObj, false);
×
702
}
×
703

704
void taosCacheCleanup(SCacheObj *pCacheObj) {
7,479✔
705
  if (pCacheObj == NULL) {
7,479!
706
    return;
×
707
  }
708

709
  pCacheObj->deleting = 1;
7,479✔
710

711
  // wait for the refresh thread quit before destroying the cache object.
712
  // But in the dll, the child thread will be killed before atexit takes effect.
713
  while (atomic_load_8(&pCacheObj->deleting) != 0) {
69,859✔
714
    if (refreshWorkerNormalStopped) break;
62,380!
715
    if (refreshWorkerUnexpectedStopped) return;
62,380!
716
    taosMsleep(50);
62,380✔
717
  }
718

719
  uTrace("cache:%s will be cleaned up", pCacheObj->name);
7,479✔
720
  doCleanupDataCache(pCacheObj);
7,479✔
721
}
722

723
SCacheNode *taosCreateCacheNode(const char *key, size_t keyLen, const char *pData, size_t size, uint64_t duration) {
343,799✔
724
  size_t sizeInBytes = size + sizeof(SCacheNode) + keyLen;
343,799✔
725

726
  SCacheNode *pNewNode = taosMemoryCalloc(1, sizeInBytes);
343,799!
727
  if (pNewNode == NULL) {
343,981!
728
    uError("failed to allocate memory, reason:%s", strerror(ERRNO));
×
729
    terrno = TSDB_CODE_OUT_OF_MEMORY;
×
730
    return NULL;
×
731
  }
732

733
  pNewNode->data = (char *)pNewNode + sizeof(SCacheNode);
343,981✔
734
  pNewNode->dataLen = size;
343,981✔
735
  memcpy(pNewNode->data, pData, size);
343,981✔
736

737
  pNewNode->key = (char *)pNewNode + sizeof(SCacheNode) + size;
343,981✔
738
  pNewNode->keyLen = (uint16_t)keyLen;
343,981✔
739

740
  memcpy(pNewNode->key, key, keyLen);
343,981✔
741

742
  pNewNode->addedTime = (uint64_t)taosGetTimestampMs();
344,195✔
743
  pNewNode->lifespan = duration;
344,195✔
744
  pNewNode->expireTime = pNewNode->addedTime + pNewNode->lifespan;
344,195✔
745
  pNewNode->signature = pNewNode;
344,195✔
746
  pNewNode->size = (uint32_t)sizeInBytes;
344,195✔
747

748
  return pNewNode;
344,195✔
749
}
750

751
void taosAddToTrashcan(SCacheObj *pCacheObj, SCacheNode *pNode) {
13✔
752
  if (pNode->inTrashcan) { /* node is already in trash */
13!
753
    // A S S E R T(pNode->pTNodeHeader != NULL && pNode->pTNodeHeader->pData == pNode);
754
    return;
×
755
  }
756

757
  STrashElem *pElem = taosMemoryCalloc(1, sizeof(STrashElem));
13!
758
  if (!pElem) {
13!
759
    uError("cache:%s key:%p, %p move to trashcan failed since %s, numOfElem in trashcan:%d", pCacheObj->name,
×
760
           pNode->key, pNode->data, terrstr(), pCacheObj->numOfElemsInTrash);
761
    return;
×
762
  }
763
  __trashcan_wr_lock(pCacheObj);
764
  pElem->pData = pNode;
13✔
765
  pElem->prev = NULL;
13✔
766
  pElem->next = NULL;
13✔
767
  pNode->inTrashcan = true;
13✔
768
  pNode->pTNodeHeader = pElem;
13✔
769

770
  pElem->next = pCacheObj->pTrash;
13✔
771
  if (pCacheObj->pTrash) {
13✔
772
    pCacheObj->pTrash->prev = pElem;
3✔
773
  }
774

775
  pCacheObj->pTrash = pElem;
13✔
776
  pCacheObj->numOfElemsInTrash++;
13✔
777
  __trashcan_unlock(pCacheObj);
778

779
  uDebug("cache:%s key:%p, %p move to trashcan, pTrashElem:%p, numOfElem in trashcan:%d", pCacheObj->name, pNode->key,
13✔
780
         pNode->data, pElem, pCacheObj->numOfElemsInTrash);
781
}
782

783
void taosTrashcanEmpty(SCacheObj *pCacheObj, bool force) {
62,524✔
784
  __trashcan_wr_lock(pCacheObj);
785

786
  if (pCacheObj->numOfElemsInTrash == 0) {
62,524✔
787
    if (pCacheObj->pTrash != NULL) {
62,514!
788
      pCacheObj->pTrash = NULL;
×
789
      uError("cache:%s, key:inconsistency data in cache, numOfElem in trashcan:%d", pCacheObj->name,
×
790
             pCacheObj->numOfElemsInTrash);
791
    }
792

793
    __trashcan_unlock(pCacheObj);
794
    return;
62,514✔
795
  }
796

797
  const char *stat[] = {"false", "true"};
10✔
798
  uDebug("cache:%s start to cleanup trashcan, numOfElem in trashcan:%d, free:%s", pCacheObj->name,
10✔
799
         pCacheObj->numOfElemsInTrash, (force ? stat[1] : stat[0]));
800

801
  STrashElem *pElem = pCacheObj->pTrash;
10✔
802
  while (pElem) {
23✔
803
    if (force || (T_REF_VAL_GET(pElem->pData) == 0)) {
13!
804
      uDebug("cache:%s, key:%p, %p removed from trashcan. numOfElem in trashcan:%d", pCacheObj->name, pElem->pData->key,
13✔
805
             pElem->pData->data, pCacheObj->numOfElemsInTrash - 1);
806

807
      (void)doRemoveElemInTrashcan(pCacheObj, pElem);
808
      doDestroyTrashcanElem(pCacheObj, pElem);
809
      pElem = pCacheObj->pTrash;
13✔
810
    } else {
811
      pElem = pElem->next;
×
812
    }
813
  }
814

815
  __trashcan_unlock(pCacheObj);
816
}
817

818
void doCleanupDataCache(SCacheObj *pCacheObj) {
7,479✔
819
  SCacheObjTravSup sup = {.pCacheObj = pCacheObj, .fp = NULL, .time = taosGetTimestampMs()};
7,479✔
820
  doTraverseElems(pCacheObj, doRemoveNodeFn, &sup);
7,479✔
821

822
  // todo memory leak if there are object with refcount greater than 0 in hash table?
823
  taosTrashcanEmpty(pCacheObj, true);
7,479✔
824

825
  __trashcan_lock_destroy(pCacheObj);
826

827
  taosMemoryFreeClear(pCacheObj->pEntryList);
7,479!
828
  taosMemoryFreeClear(pCacheObj->name);
7,479!
829
  taosMemoryFree(pCacheObj);
7,479!
830
}
7,479✔
831

832
static void doCacheRefresh(SCacheObj *pCacheObj, int64_t time, __cache_trav_fn_t fp, void *param1) {
55,045✔
833
  SCacheObjTravSup sup = {.pCacheObj = pCacheObj, .fp = fp, .time = time, .param1 = param1};
55,045✔
834
  doTraverseElems(pCacheObj, doRemoveExpiredFn, &sup);
55,045✔
835
}
55,045✔
836

837
void taosCacheRefreshWorkerUnexpectedStopped(void) {
×
838
  if (!refreshWorkerNormalStopped) {
×
839
    refreshWorkerUnexpectedStopped = true;
×
840
  }
841
}
×
842

843
void *taosCacheTimedRefresh(void *handle) {
2,374✔
844
  uDebug("cache refresh thread starts");
2,374✔
845
  setThreadName("cacheRefresh");
2,374✔
846

847
  const int32_t SLEEP_DURATION = 500;  // 500 ms
2,374✔
848
  int64_t       count = 0;
2,374✔
849
#ifdef WINDOWS
850
  if (taosCheckCurrentInDll()) {
851
    atexit(taosCacheRefreshWorkerUnexpectedStopped);
852
  }
853
#endif
854

855
  while (1) {
193,434✔
856
    taosMsleep(SLEEP_DURATION);
195,808✔
857
    if (stopRefreshWorker) {
195,807✔
858
      goto _end;
2,373✔
859
    }
860

861
    (void)taosThreadMutexLock(&guard);
193,434✔
862
    size_t size = taosArrayGetSize(pCacheArrayList);
193,434✔
863
    (void)taosThreadMutexUnlock(&guard);
193,434✔
864

865
    count += 1;
193,434✔
866

867
    for (int32_t i = 0; i < size; ++i) {
713,289✔
868
      (void)taosThreadMutexLock(&guard);
519,855✔
869
      SCacheObj *pCacheObj = taosArrayGetP(pCacheArrayList, i);
519,855✔
870

871
      if (pCacheObj == NULL) {
519,855!
872
        uError("object is destroyed. ignore and try next");
×
873
        (void)taosThreadMutexUnlock(&guard);
×
874
        continue;
×
875
      }
876

877
      // check if current cache object will be deleted every 500ms.
878
      if (pCacheObj->deleting) {
519,855✔
879
        taosArrayRemove(pCacheArrayList, i);
7,479✔
880
        size = taosArrayGetSize(pCacheArrayList);
7,479✔
881

882
        uDebug("%s is destroying, remove it from refresh list, remain cache obj:%" PRIzu, pCacheObj->name, size);
7,479✔
883
        pCacheObj->deleting = 0;  // reset the deleting flag to enable pCacheObj to continue releasing resources.
7,479✔
884

885
        (void)taosThreadMutexUnlock(&guard);
7,479✔
886
        continue;
7,479✔
887
      }
888

889
      (void)taosThreadMutexUnlock(&guard);
512,376✔
890

891
      if ((count % pCacheObj->checkTick) != 0) {
512,376✔
892
        continue;
441,313✔
893
      }
894

895
      size_t elemInHash = pCacheObj->numOfElems;
71,063✔
896
      if (elemInHash + pCacheObj->numOfElemsInTrash == 0) {
71,063✔
897
        continue;
16,018✔
898
      }
899

900
      uDebug("%s refresh thread scan", pCacheObj->name);
55,045✔
901
      pCacheObj->statistics.refreshCount++;
55,045✔
902

903
      // refresh data in hash table
904
      if (elemInHash > 0) {
55,045!
905
        int64_t now = taosGetTimestampMs();
55,045✔
906
        doCacheRefresh(pCacheObj, now, NULL, NULL);
55,045✔
907
      }
908

909
      taosTrashcanEmpty(pCacheObj, false);
55,045✔
910
    }
911
  }
912

913
_end:
2,373✔
914
  taosArrayDestroy(pCacheArrayList);
2,373✔
915

916
  pCacheArrayList = NULL;
2,373✔
917
  (void)taosThreadMutexDestroy(&guard);
2,373✔
918
  refreshWorkerNormalStopped = true;
2,373✔
919

920
  uDebug("cache refresh thread quits");
2,373✔
921
  return NULL;
2,373✔
922
}
923

924
void taosCacheRefresh(SCacheObj *pCacheObj, __cache_trav_fn_t fp, void *param1) {
×
925
  if (pCacheObj == NULL) {
×
926
    return;
×
927
  }
928

929
  int64_t now = taosGetTimestampMs();
×
930
  doCacheRefresh(pCacheObj, now, fp, param1);
×
931
}
932

933
void taosStopCacheRefreshWorker(void) {
3,103✔
934
  stopRefreshWorker = true;
3,103✔
935
  TdThreadOnce tmp = PTHREAD_ONCE_INIT;
3,103✔
936
  if (memcmp(&cacheThreadInit, &tmp, sizeof(TdThreadOnce)) != 0) (void)taosThreadJoin(cacheRefreshWorker, NULL);
3,103✔
937
  taosArrayDestroy(pCacheArrayList);
3,103✔
938
}
3,103✔
939

940
size_t taosCacheGetNumOfObj(const SCacheObj *pCacheObj) { return pCacheObj->numOfElems + pCacheObj->numOfElemsInTrash; }
16✔
941

942
SCacheIter *taosCacheCreateIter(const SCacheObj *pCacheObj) {
28,841✔
943
  SCacheIter *pIter = taosMemoryCalloc(1, sizeof(SCacheIter));
28,841!
944
  if (pIter) {
28,868✔
945
    pIter->pCacheObj = (SCacheObj *)pCacheObj;
28,866✔
946
    pIter->entryIndex = -1;
28,866✔
947
    pIter->index = -1;
28,866✔
948
  }
949
  return pIter;
28,868✔
950
}
951

952
bool taosCacheIterNext(SCacheIter *pIter) {
1,216,627✔
953
  SCacheObj *pCacheObj = pIter->pCacheObj;
1,216,627✔
954

955
  if (pIter->index + 1 >= pIter->numOfObj) {
1,216,627✔
956
    // release the reference for all objects in the snapshot
957
    for (int32_t i = 0; i < pIter->numOfObj; ++i) {
2,356,304✔
958
      char *p = pIter->pCurrent[i]->data;
1,189,934✔
959
      taosCacheRelease(pCacheObj, (void **)&p, false);
1,189,934✔
960
      pIter->pCurrent[i] = NULL;
1,194,478✔
961
    }
962

963
    if (pIter->entryIndex + 1 >= pCacheObj->capacity) {
1,166,370!
964
      return false;
×
965
    }
966

967
    while (1) {
116,307,955✔
968
      pIter->entryIndex++;
117,474,325✔
969
      if (pIter->entryIndex >= pCacheObj->capacity) {
117,474,325✔
970
        return false;
28,882✔
971
      }
972

973
      SCacheEntry *pEntry = &pCacheObj->pEntryList[pIter->entryIndex];
117,445,443✔
974
      taosRLockLatch(&pEntry->latch);
117,445,443✔
975

976
      if (pEntry->num == 0) {
116,919,369✔
977
        taosRUnLockLatch(&pEntry->latch);
115,780,553✔
978
        continue;
116,307,955✔
979
      }
980

981
      if (pIter->numOfObj < pEntry->num) {
1,138,816✔
982
        char *tmp = taosMemoryRealloc(pIter->pCurrent, pEntry->num * POINTER_BYTES);
74,340!
983
        if (tmp == NULL) {
74,328!
984
          terrno = TSDB_CODE_OUT_OF_MEMORY;
×
985
          taosRUnLockLatch(&pEntry->latch);
×
986
          return false;
×
987
        }
988

989
        pIter->pCurrent = (SCacheNode **)tmp;
74,328✔
990
      }
991

992
      SCacheNode *pNode = pEntry->next;
1,138,804✔
993
      for (int32_t i = 0; i < pEntry->num; ++i) {
2,333,886✔
994
        pIter->pCurrent[i] = pNode;
1,195,054✔
995
        int32_t ref = T_REF_INC(pIter->pCurrent[i]);
1,195,054✔
996
        pNode = pNode->pNext;
1,195,082✔
997
      }
998

999
      pIter->numOfObj = pEntry->num;
1,138,832✔
1000
      taosRUnLockLatch(&pEntry->latch);
1,138,832✔
1001

1002
      pIter->index = -1;
1,138,849✔
1003
      break;
1,138,849✔
1004
    }
1005
  }
1006

1007
  pIter->index += 1;
1,193,650✔
1008
  return true;
1,193,650✔
1009
}
1010

1011
void *taosCacheIterGetData(const SCacheIter *pIter, size_t *len) {
1,194,700✔
1012
  SCacheNode *pNode = pIter->pCurrent[pIter->index];
1,194,700✔
1013
  *len = pNode->dataLen;
1,194,700✔
1014
  return pNode->data;
1,194,700✔
1015
}
1016

1017
void *taosCacheIterGetKey(const SCacheIter *pIter, size_t *len) {
×
1018
  SCacheNode *pNode = pIter->pCurrent[pIter->index];
×
1019
  *len = pNode->keyLen;
×
1020
  return pNode->key;
×
1021
}
1022

1023
void taosCacheDestroyIter(SCacheIter *pIter) {
28,872✔
1024
  for (int32_t i = 0; i < pIter->numOfObj; ++i) {
68,546✔
1025
    if (!pIter->pCurrent[i]) continue;
39,674!
1026
    char *p = pIter->pCurrent[i]->data;
×
1027
    taosCacheRelease(pIter->pCacheObj, (void **)&p, false);
×
1028
    pIter->pCurrent[i] = NULL;
×
1029
  }
1030
  taosMemoryFreeClear(pIter->pCurrent);
28,872!
1031
  taosMemoryFreeClear(pIter);
28,873!
1032
}
28,879✔
1033

1034
void taosCacheTryExtendLifeSpan(SCacheObj *pCacheObj, void **data) {
443,848✔
1035
  if (!pCacheObj || !(*data)) return;
443,848!
1036

1037
  SCacheNode *pNode = (SCacheNode *)((char *)(*data) - sizeof(SCacheNode));
443,856✔
1038
  if (pNode->signature != pNode) return;
443,856!
1039

1040
  if (!pNode->inTrashcan) {
443,856✔
1041
    atomic_store_64(&pNode->expireTime, pNode->lifespan + taosGetTimestampMs());
887,702✔
1042
    uDebug("cache:%s, data:%p extend expire time: %" PRId64, pCacheObj->name, pNode->data, pNode->expireTime);
443,856✔
1043
  }
1044
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc