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

S
hash  
Shengliang Guan 已提交
16
#define _DEFAULT_SOURCE
H
Haojun Liao 已提交
17
#include "thash.h"
H
Haojun Liao 已提交
18 19
#include "taoserror.h"
#include "os.h"
S
hash  
Shengliang Guan 已提交
20
#include "tlog.h"
21

22
// the add ref count operation may trigger the warning if the reference count is greater than the MAX_WARNING_REF_COUNT
H
Haojun Liao 已提交
23 24 25 26 27 28 29 30 31 32 33 34 35 36
#define MAX_WARNING_REF_COUNT    10000
#define HASH_MAX_CAPACITY        (1024 * 1024 * 16)
#define HASH_DEFAULT_LOAD_FACTOR (0.75)
#define HASH_INDEX(v, c)         ((v) & ((c)-1))

#define HASH_NEED_RESIZE(_h) ((_h)->size >= (_h)->capacity * HASH_DEFAULT_LOAD_FACTOR)

#define GET_HASH_NODE_KEY(_n)  ((char*)(_n) + sizeof(SHashNode) + (_n)->dataLen)
#define GET_HASH_NODE_DATA(_n) ((char*)(_n) + sizeof(SHashNode))
#define GET_HASH_PNODE(_n)     ((SHashNode *)((char*)(_n) - sizeof(SHashNode)))

#define FREE_HASH_NODE(_n) \
  do {                     \
    tfree(_n);             \
H
Haojun Liao 已提交
37 38
  } while (0);

H
Haojun Liao 已提交
39 40 41 42 43 44 45 46 47 48
struct SHashNode {
  SHashNode        *next;
  uint32_t          hashVal;  // the hash value of key
  uint32_t          dataLen;  // length of data
  uint32_t          keyLen;   // length of the key
  uint16_t          refCount; // reference count
  int8_t            removed;  // flag to indicate removed
  char              data[];
};

H
Haojun Liao 已提交
49
typedef struct SHashEntry {
H
Haojun Liao 已提交
50 51 52
  int32_t           num;      // number of elements in current entry
  SRWLatch          latch;    // entry latch
  SHashNode        *next;
H
Haojun Liao 已提交
53 54
} SHashEntry;

H
Haojun Liao 已提交
55 56 57
struct SHashObj {
  SHashEntry **     hashList;
  size_t            capacity;      // number of slots
wafwerar's avatar
wafwerar 已提交
58
  int64_t           size;          // number of elements in hash table
H
Haojun Liao 已提交
59 60 61 62 63 64 65 66 67
  _hash_fn_t        hashFp;        // hash function
  _equal_fn_t       equalFp;       // equal function
  _hash_free_fn_t   freeFp;        // hash node free callback function
  SRWLatch          lock;          // read-write spin lock
  SHashLockTypeE    type;          // lock type
  bool              enableUpdate;  // enable update
  SArray *          pMemBlock;     // memory block allocated for SHashEntry
  _hash_before_fn_t callbackFp;    // function invoked before return the value to caller
};
H
Haojun Liao 已提交
68 69 70 71 72 73 74 75 76 77 78 79 80

/*
 * Function definition
 */
static FORCE_INLINE void taosHashWLock(SHashObj *pHashObj) {
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }
  taosWLockLatch(&pHashObj->lock);
}

static FORCE_INLINE void taosHashWUnlock(SHashObj *pHashObj) {
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
81 82
    return;
  }
H
Haojun Liao 已提交
83 84

  taosWUnLockLatch(&pHashObj->lock);
85 86
}

H
Haojun Liao 已提交
87 88
static FORCE_INLINE void taosHashRLock(SHashObj *pHashObj) {
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
89 90
    return;
  }
H
Haojun Liao 已提交
91 92

  taosRLockLatch(&pHashObj->lock);
93 94
}

H
Haojun Liao 已提交
95 96
static FORCE_INLINE void taosHashRUnlock(SHashObj *pHashObj) {
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
97 98
    return;
  }
H
Haojun Liao 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

  taosRUnLockLatch(&pHashObj->lock);
}

static FORCE_INLINE void taosHashEntryWLock(const SHashObj *pHashObj, SHashEntry* pe) {
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }
  taosWLockLatch(&pe->latch);
}

static FORCE_INLINE void taosHashEntryWUnlock(const SHashObj *pHashObj, SHashEntry* pe) {
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }

  taosWUnLockLatch(&pe->latch);
}

static FORCE_INLINE void taosHashEntryRLock(const SHashObj *pHashObj, SHashEntry* pe) {
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }

  taosRLockLatch(&pe->latch);
124 125
}

H
Haojun Liao 已提交
126 127
static FORCE_INLINE void taosHashEntryRUnlock(const SHashObj *pHashObj, SHashEntry* pe) {
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
128 129
    return;
  }
H
Haojun Liao 已提交
130 131

  taosRUnLockLatch(&pe->latch);
132 133 134
}

static FORCE_INLINE int32_t taosHashCapacity(int32_t length) {
H
Haojun Liao 已提交
135
  int32_t len = MIN(length, HASH_MAX_CAPACITY);
136

S
Shengliang Guan 已提交
137
  int32_t i = 4;
138
  while (i < len) i = (i << 1u);
139 140 141
  return i;
}

H
Haojun Liao 已提交
142 143
static FORCE_INLINE SHashNode *
doSearchInEntryList(SHashObj *pHashObj, SHashEntry *pe, const void *key, size_t keyLen, uint32_t hashVal) {
H
Haojun Liao 已提交
144
  SHashNode *pNode = pe->next;
H
Haojun Liao 已提交
145
  while (pNode) {
H
Haojun Liao 已提交
146 147
    if ((pNode->keyLen == keyLen) &&
        ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) &&
S
hash  
Shengliang Guan 已提交
148
        pNode->removed == 0) {
H
Haojun Liao 已提交
149 150 151 152 153 154 155 156 157 158
      assert(pNode->hashVal == hashVal);
      break;
    }

    pNode = pNode->next;
  }

  return pNode;
}

159
/**
H
Haojun Liao 已提交
160
 * resize the hash list if the threshold is reached
161
 *
H
hjxilinx 已提交
162
 * @param pHashObj
163
 */
H
hjLiao 已提交
164
static void taosHashTableResize(SHashObj *pHashObj);
165

H
hjLiao 已提交
166
/**
H
Haojun Liao 已提交
167 168
 * allocate and initialize a hash node
 *
H
hjLiao 已提交
169 170
 * @param key      key of object for hash, usually a null-terminated string
 * @param keyLen   length of key
H
Haojun Liao 已提交
171
 * @param pData    data to be stored in hash node
H
hjLiao 已提交
172 173 174 175
 * @param dsize    size of data
 * @return         SHashNode
 */
static SHashNode *doCreateHashNode(const void *key, size_t keyLen, const void *pData, size_t dsize, uint32_t hashVal);
176

H
hjLiao 已提交
177
/**
H
Haojun Liao 已提交
178
 * update the hash node
H
hjLiao 已提交
179
 *
H
Haojun Liao 已提交
180 181 182 183 184
 * @param pHashObj   hash table object
 * @param pe         hash table entry to operate on
 * @param prev       previous node
 * @param pNode      the old node with requested key
 * @param pNewNode   the new node with requested key
H
hjLiao 已提交
185
 */
H
Haojun Liao 已提交
186
static FORCE_INLINE void doUpdateHashNode(SHashObj *pHashObj, SHashEntry* pe, SHashNode* prev, SHashNode *pNode, SHashNode *pNewNode) {
H
Haojun Liao 已提交
187
  assert(pNode->keyLen == pNewNode->keyLen);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
188

wafwerar's avatar
wafwerar 已提交
189
  atomic_sub_fetch_16(&pNode->refCount, 1);
H
Haojun Liao 已提交
190 191
  if (prev != NULL) {
    prev->next = pNewNode;
192 193
  } else {
    pe->next = pNewNode;
H
Haojun Liao 已提交
194
  }
H
Haojun Liao 已提交
195

H
Haojun Liao 已提交
196
  if (pNode->refCount <= 0) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
197
    pNewNode->next = pNode->next;
H
Haojun Liao 已提交
198
    FREE_HASH_NODE(pNode);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
199
  } else {
S
hash  
Shengliang Guan 已提交
200
    pNewNode->next = pNode;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
201
    pe->num++;
H
Haojun Liao 已提交
202
    atomic_add_fetch_64(&pHashObj->size, 1);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
203
  }
H
Haojun Liao 已提交
204
}
205

H
hjLiao 已提交
206 207 208
/**
 * insert the hash node at the front of the linked list
 *
H
Haojun Liao 已提交
209 210
 * @param pHashObj   hash table object
 * @param pNode      the old node with requested key
H
hjLiao 已提交
211
 */
H
Haojun Liao 已提交
212
static void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode);
213

214 215 216 217 218 219 220 221
/**
 * Check whether the hash table is empty or not.
 *
 * @param pHashObj the hash table object
 * @return if the hash table is empty or not
 */
static FORCE_INLINE bool taosHashTableEmpty(const SHashObj *pHashObj);

222
/**
H
Haojun Liao 已提交
223
 *
224 225
 * @param pHashObj
 * @return
226
 */
227 228 229 230
static FORCE_INLINE bool taosHashTableEmpty(const SHashObj *pHashObj) {
  return taosHashGetSize(pHashObj) == 0;
}

H
Haojun Liao 已提交
231
SHashObj *taosHashInit(size_t capacity, _hash_fn_t fn, bool update, SHashLockTypeE type) {
H
Haojun Liao 已提交
232 233 234 235 236
  if (fn == NULL) {
    assert(0);
    return NULL;
  }

237 238
  if (capacity == 0) {
    capacity = 4;
239 240
  }

H
hjxilinx 已提交
241 242
  SHashObj *pHashObj = (SHashObj *)calloc(1, sizeof(SHashObj));
  if (pHashObj == NULL) {
H
Haojun Liao 已提交
243
    terrno = TSDB_CODE_OUT_OF_MEMORY;
244 245 246 247
    return NULL;
  }

  // the max slots is not defined by user
S
Shengliang Guan 已提交
248
  pHashObj->capacity = taosHashCapacity((int32_t)capacity);
H
Haojun Liao 已提交
249

250
  pHashObj->equalFp = memcmp;
H
Haojun Liao 已提交
251
  pHashObj->hashFp  = fn;
H
Haojun Liao 已提交
252
  pHashObj->type = type;
H
Haojun Liao 已提交
253
  pHashObj->enableUpdate = update;
254

H
Haojun Liao 已提交
255 256
  ASSERT((pHashObj->capacity & (pHashObj->capacity - 1)) == 0);

H
Haojun Liao 已提交
257
  pHashObj->hashList = (SHashEntry **)calloc(pHashObj->capacity, sizeof(void *));
H
hjxilinx 已提交
258 259
  if (pHashObj->hashList == NULL) {
    free(pHashObj);
H
Haojun Liao 已提交
260
    terrno = TSDB_CODE_OUT_OF_MEMORY;
261
    return NULL;
H
Haojun Liao 已提交
262
  }
H
Haojun Liao 已提交
263

H
Haojun Liao 已提交
264 265 266 267 268 269 270
  pHashObj->pMemBlock = taosArrayInit(8, sizeof(void *));
  if (pHashObj->pMemBlock == NULL) {
    free(pHashObj->hashList);
    free(pHashObj);
    terrno = TSDB_CODE_OUT_OF_MEMORY;
    return NULL;
  }
H
Haojun Liao 已提交
271

H
Haojun Liao 已提交
272 273 274 275 276 277 278
  void *p = calloc(pHashObj->capacity, sizeof(SHashEntry));
  if (p == NULL) {
    taosArrayDestroy(pHashObj->pMemBlock);
    free(pHashObj->hashList);
    free(pHashObj);
    terrno = TSDB_CODE_OUT_OF_MEMORY;
    return NULL;
279
  }
H
hjxilinx 已提交
280

H
Haojun Liao 已提交
281 282 283 284 285
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
    pHashObj->hashList[i] = (void *)((char *)p + i * sizeof(SHashEntry));
  }

  taosArrayPush(pHashObj->pMemBlock, &p);
H
hjxilinx 已提交
286
  return pHashObj;
287 288
}

289 290 291
void taosHashSetEqualFp(SHashObj *pHashObj, _equal_fn_t fp) {
  if (pHashObj != NULL && fp != NULL) {
    pHashObj->equalFp = fp;
S
hash  
Shengliang Guan 已提交
292 293
  }
}
294

H
Haojun Liao 已提交
295 296 297 298 299 300
void taosHashSetFreeFp(SHashObj *pHashObj, _hash_free_fn_t fp) {
  if (pHashObj != NULL && fp != NULL) {
    pHashObj->freeFp = fp;
  }
}

301
int32_t taosHashGetSize(const SHashObj *pHashObj) {
H
Haojun Liao 已提交
302
  if (pHashObj == NULL) {
303 304
    return 0;
  }
wafwerar's avatar
wafwerar 已提交
305
  return (int32_t)atomic_load_64((int64_t*)&pHashObj->size);
306 307
}

X
Xiaoyu Wang 已提交
308
int32_t taosHashPut(SHashObj *pHashObj, const void *key, size_t keyLen, const void *data, size_t size) {
H
Haojun Liao 已提交
309
  if (pHashObj == NULL || key == NULL || keyLen == 0) {
H
Haojun Liao 已提交
310 311
    return -1;
  }
312

S
Shengliang Guan 已提交
313
  uint32_t   hashVal = (*pHashObj->hashFp)(key, (uint32_t)keyLen);
H
Haojun Liao 已提交
314 315 316 317
  SHashNode *pNewNode = doCreateHashNode(key, keyLen, data, size, hashVal);
  if (pNewNode == NULL) {
    return -1;
  }
318

H
Haojun Liao 已提交
319 320
  // need the resize process, write lock applied
  if (HASH_NEED_RESIZE(pHashObj)) {
H
Haojun Liao 已提交
321
    taosHashWLock(pHashObj);
H
Haojun Liao 已提交
322
    taosHashTableResize(pHashObj);
H
Haojun Liao 已提交
323
    taosHashWUnlock(pHashObj);
H
Haojun Liao 已提交
324 325
  }

326
  // disable resize
H
Haojun Liao 已提交
327
  taosHashRLock(pHashObj);
H
Haojun Liao 已提交
328

H
Haojun Liao 已提交
329
  int32_t     slot = HASH_INDEX(hashVal, pHashObj->capacity);
H
Haojun Liao 已提交
330
  SHashEntry *pe = pHashObj->hashList[slot];
331

H
Haojun Liao 已提交
332
  taosHashEntryWLock(pHashObj, pe);
333

H
Haojun Liao 已提交
334
  SHashNode *pNode = pe->next;
335
#if 0
H
Haojun Liao 已提交
336 337 338 339 340
  if (pe->num > 0) {
    assert(pNode != NULL);
  } else {
    assert(pNode == NULL);
  }
341
#endif
H
Haojun Liao 已提交
342

H
Haojun Liao 已提交
343
  SHashNode* prev = NULL;
H
Haojun Liao 已提交
344
  while (pNode) {
H
Haojun Liao 已提交
345 346
    if ((pNode->keyLen == keyLen) &&
        (*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0 &&
S
hash  
Shengliang Guan 已提交
347
        pNode->removed == 0) {
H
Haojun Liao 已提交
348 349
      assert(pNode->hashVal == hashVal);
      break;
350 351
    }

H
Haojun Liao 已提交
352
    prev = pNode;
H
Haojun Liao 已提交
353 354 355 356 357
    pNode = pNode->next;
  }

  if (pNode == NULL) {
    // no data in hash table with the specified key, add it into hash table
H
Haojun Liao 已提交
358
    pushfrontNodeInEntryList(pe, pNewNode);
H
Haojun Liao 已提交
359
    assert(pe->next != NULL);
H
Haojun Liao 已提交
360

H
Haojun Liao 已提交
361
    taosHashEntryWUnlock(pHashObj, pe);
H
Haojun Liao 已提交
362 363

    // enable resize
H
Haojun Liao 已提交
364 365
    taosHashRUnlock(pHashObj);
    atomic_add_fetch_64(&pHashObj->size, 1);
S
hash  
Shengliang Guan 已提交
366

H
Haojun Liao 已提交
367
    return 0;
368
  } else {
H
Haojun Liao 已提交
369 370
    // not support the update operation, return error
    if (pHashObj->enableUpdate) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
371
      doUpdateHashNode(pHashObj, pe, prev, pNode, pNewNode);
372
    } else {
H
Haojun Liao 已提交
373
      FREE_HASH_NODE(pNewNode);
H
Haojun Liao 已提交
374 375
    }

H
Haojun Liao 已提交
376
    taosHashEntryWUnlock(pHashObj, pe);
H
Haojun Liao 已提交
377 378

    // enable resize
H
Haojun Liao 已提交
379
    taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
380
    return pHashObj->enableUpdate ? 0 : -2;
H
Haojun Liao 已提交
381
  }
382 383
}

H
Haojun Liao 已提交
384
static void* taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void** d, int32_t* size, bool addRef);
D
dapan1121 已提交
385

H
hjLiao 已提交
386
void *taosHashGet(SHashObj *pHashObj, const void *key, size_t keyLen) {
H
Haojun Liao 已提交
387 388
  void* p = NULL;
  return taosHashGetImpl(pHashObj, key, keyLen, &p, 0, false);
H
Haojun Liao 已提交
389
}
H
Haojun Liao 已提交
390

H
Haojun Liao 已提交
391 392 393 394 395
int32_t taosHashGetDup(SHashObj *pHashObj, const void *key, size_t keyLen, void *destBuf) {
  terrno = 0;
  /*char* p = */taosHashGetImpl(pHashObj, key, keyLen, &destBuf, 0, false);
  return terrno;
}
396

H
Haojun Liao 已提交
397 398
int32_t taosHashGetDup_m(SHashObj *pHashObj, const void *key, size_t keyLen, void **destBuf, int32_t* size) {
  terrno = 0;
399

H
Haojun Liao 已提交
400 401
  /*char* p = */taosHashGetImpl(pHashObj, key, keyLen, destBuf, size, false);
  return terrno;
402
}
H
Haojun Liao 已提交
403

H
Haojun Liao 已提交
404 405
void* taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void** d, int32_t* size, bool addRef) {
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj) || keyLen == 0 || key == NULL) {
H
Haojun Liao 已提交
406 407 408
    return NULL;
  }

S
Shengliang Guan 已提交
409
  uint32_t hashVal = (*pHashObj->hashFp)(key, (uint32_t)keyLen);
410

H
Haojun Liao 已提交
411
  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
412
  taosHashRLock(pHashObj);
H
Haojun Liao 已提交
413

H
Haojun Liao 已提交
414 415
  int32_t     slot = HASH_INDEX(hashVal, pHashObj->capacity);
  SHashEntry *pe = pHashObj->hashList[slot];
416

H
Haojun Liao 已提交
417 418
  // no data, return directly
  if (atomic_load_32(&pe->num) == 0) {
H
Haojun Liao 已提交
419
    taosHashRUnlock(pHashObj);
420 421
    return NULL;
  }
H
Haojun Liao 已提交
422 423

  char *data = NULL;
H
Haojun Liao 已提交
424
  taosHashEntryRLock(pHashObj, pe);
H
Haojun Liao 已提交
425

H
Haojun Liao 已提交
426
#if 0
H
Haojun Liao 已提交
427 428 429 430 431
  if (pe->num > 0) {
    assert(pe->next != NULL);
  } else {
    assert(pe->next == NULL);
  }
H
Haojun Liao 已提交
432
#endif
H
Haojun Liao 已提交
433

434
  SHashNode *pNode = doSearchInEntryList(pHashObj, pe, key, keyLen, hashVal);
H
Haojun Liao 已提交
435
  if (pNode != NULL) {
H
Haojun Liao 已提交
436 437
    if (pHashObj->callbackFp != NULL) {
      pHashObj->callbackFp(GET_HASH_NODE_DATA(pNode));
H
Haojun Liao 已提交
438
    }
H
Haojun Liao 已提交
439

H
Haojun Liao 已提交
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
    if (size != NULL) {
      if (*d == NULL) {
        *size =  pNode->dataLen;
        *d = calloc(1, *size);
        if (*d == NULL) {
          terrno = TSDB_CODE_OUT_OF_MEMORY;
          return NULL;
        }
      } else if (*size < pNode->dataLen) {
        *size =  pNode->dataLen;
        char* tmp = realloc(*d, *size);
        if (tmp == NULL) {
          terrno = TSDB_CODE_OUT_OF_MEMORY;
          return NULL;
        }

        *d = tmp;
      }
    }

    if (addRef) {
      atomic_add_fetch_16(&pNode->refCount, 1);
H
Haojun Liao 已提交
462
    }
463

H
Haojun Liao 已提交
464 465
    if (*d != NULL) {
      memcpy(*d, GET_HASH_NODE_DATA(pNode), pNode->dataLen);
D
dapan1121 已提交
466 467
    }

468
    data = GET_HASH_NODE_DATA(pNode);
H
Haojun Liao 已提交
469 470
  }

H
Haojun Liao 已提交
471 472
  taosHashEntryRUnlock(pHashObj, pe);
  taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
473 474

  return data;
475 476
}

H
Haojun Liao 已提交
477
int32_t taosHashRemove(SHashObj *pHashObj, const void *key, size_t keyLen) {
H
Haojun Liao 已提交
478
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj) || key == NULL || keyLen == 0) {
H
Haojun Liao 已提交
479 480 481
    return -1;
  }

S
Shengliang Guan 已提交
482
  uint32_t hashVal = (*pHashObj->hashFp)(key, (uint32_t)keyLen);
H
Haojun Liao 已提交
483

H
Haojun Liao 已提交
484
  // disable the resize process
H
Haojun Liao 已提交
485
  taosHashRLock(pHashObj);
H
Haojun Liao 已提交
486

H
Haojun Liao 已提交
487
  int32_t     slot = HASH_INDEX(hashVal, pHashObj->capacity);
H
Haojun Liao 已提交
488 489
  SHashEntry *pe = pHashObj->hashList[slot];

H
Haojun Liao 已提交
490
  taosHashEntryWLock(pHashObj, pe);
491 492

  // double check after locked
H
Haojun Liao 已提交
493
  if (pe->num == 0) {
H
Haojun Liao 已提交
494 495
    assert(pe->next == NULL);

H
Haojun Liao 已提交
496 497
    taosHashEntryWUnlock(pHashObj, pe);
    taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
498
    return -1;
H
Haojun Liao 已提交
499 500
  }

H
Haojun Liao 已提交
501
  int code = -1;
H
Haojun Liao 已提交
502
  SHashNode *pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
503
  SHashNode *prevNode = NULL;
H
Haojun Liao 已提交
504

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
505
  while (pNode) {
H
Haojun Liao 已提交
506 507 508 509
    if ((pNode->keyLen == keyLen) &&
        ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) &&
        pNode->removed == 0) {
      code = 0;  // it is found
H
Haojun Liao 已提交
510

wafwerar's avatar
wafwerar 已提交
511
      atomic_sub_fetch_16(&pNode->refCount, 1);
H
Haojun Liao 已提交
512 513 514 515 516 517 518
      pNode->removed = 1;
      if (pNode->refCount <= 0) {
        if (prevNode == NULL) {
          pe->next = pNode->next;
        } else {
          prevNode->next = pNode->next;
        }
H
Haojun Liao 已提交
519

H
Haojun Liao 已提交
520 521 522
        pe->num--;
        atomic_sub_fetch_64(&pHashObj->size, 1);
        FREE_HASH_NODE(pNode);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
523
      }
H
Haojun Liao 已提交
524 525 526
    } else {
      prevNode = pNode;
      pNode = pNode->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
527
    }
H
Haojun Liao 已提交
528
  }
529

H
Haojun Liao 已提交
530 531
  taosHashEntryWUnlock(pHashObj, pe);
  taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
532

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
533
  return code;
H
Haojun Liao 已提交
534 535
}

536
void taosHashClear(SHashObj *pHashObj) {
H
Haojun Liao 已提交
537 538 539
  if (pHashObj == NULL) {
    return;
  }
540 541 542

  SHashNode *pNode, *pNext;

H
Haojun Liao 已提交
543
  taosHashWLock(pHashObj);
544

545 546 547
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
    SHashEntry *pEntry = pHashObj->hashList[i];
    if (pEntry->num == 0) {
H
Haojun Liao 已提交
548
      assert(pEntry->next == NULL);
549 550
      continue;
    }
551

552 553
    pNode = pEntry->next;
    assert(pNode != NULL);
H
Haojun Liao 已提交
554

555 556
    while (pNode) {
      pNext = pNode->next;
H
Haojun Liao 已提交
557
      FREE_HASH_NODE(pNode);
H
hjxilinx 已提交
558

559
      pNode = pNext;
560 561
    }

562 563
    pEntry->num = 0;
    pEntry->next = NULL;
564 565
  }

H
Haojun Liao 已提交
566 567
  pHashObj->size = 0;
  taosHashWUnlock(pHashObj);
568 569
}

H
Haojun Liao 已提交
570
// the input paras should be SHashObj **, so the origin input will be set by tfree(*pHashObj)
571 572 573 574 575
void taosHashCleanup(SHashObj *pHashObj) {
  if (pHashObj == NULL) {
    return;
  }

576
  taosHashClear(pHashObj);
577
  tfree(pHashObj->hashList);
H
Haojun Liao 已提交
578 579 580

  // destroy mem block
  size_t memBlock = taosArrayGetSize(pHashObj->pMemBlock);
H
Haojun Liao 已提交
581 582
  for (int32_t i = 0; i < memBlock; ++i) {
    void *p = taosArrayGetP(pHashObj->pMemBlock, i);
S
TD-1848  
Shengliang Guan 已提交
583
    tfree(p);
H
Haojun Liao 已提交
584 585 586
  }

  taosArrayDestroy(pHashObj->pMemBlock);
H
hjxilinx 已提交
587
  free(pHashObj);
588 589 590
}

// for profile only
H
Haojun Liao 已提交
591
int32_t taosHashGetMaxOverflowLinkLength(const SHashObj *pHashObj){
592
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
593 594
    return 0;
  }
H
hjxilinx 已提交
595

596
  int32_t num = 0;
H
hjxilinx 已提交
597

H
Haojun Liao 已提交
598
  taosHashRLock((SHashObj*) pHashObj);
H
hjxilinx 已提交
599
  for (int32_t i = 0; i < pHashObj->size; ++i) {
H
Haojun Liao 已提交
600
    SHashEntry *pEntry = pHashObj->hashList[i];
H
Haojun Liao 已提交
601 602 603

    // fine grain per entry lock is not held since this is used
    // for profiling only and doesn't need an accurate count.
H
Haojun Liao 已提交
604 605
    if (num < pEntry->num) {
      num = pEntry->num;
606 607
    }
  }
H
hjxilinx 已提交
608

H
Haojun Liao 已提交
609
  taosHashRUnlock((SHashObj*) pHashObj);
610 611
  return num;
}
H
hjLiao 已提交
612 613

void taosHashTableResize(SHashObj *pHashObj) {
H
Haojun Liao 已提交
614
  if (!HASH_NEED_RESIZE(pHashObj)) {
H
hjLiao 已提交
615 616
    return;
  }
H
Haojun Liao 已提交
617

H
Haojun Liao 已提交
618 619 620 621
  int32_t newCapacity = (int32_t)(pHashObj->capacity << 1u);
  if (newCapacity > HASH_MAX_CAPACITY) {
//    uDebug("current capacity:%zu, maximum capacity:%d, no resize applied due to limitation is reached",
//           pHashObj->capacity, HASH_MAX_CAPACITY);
H
hjLiao 已提交
622 623 624
    return;
  }

H
Haojun Liao 已提交
625
  int64_t st = taosGetTimestampUs();
H
Haojun Liao 已提交
626 627 628
  void *pNewEntryList = realloc(pHashObj->hashList, sizeof(void *) * newCapacity);
  if (pNewEntryList == NULL) {
//    uDebug("cache resize failed due to out of memory, capacity remain:%zu", pHashObj->capacity);
H
hjLiao 已提交
629 630
    return;
  }
H
Haojun Liao 已提交
631

H
Haojun Liao 已提交
632 633
  pHashObj->hashList = pNewEntryList;

H
Haojun Liao 已提交
634 635
  size_t inc = newCapacity - pHashObj->capacity;
  void * p = calloc(inc, sizeof(SHashEntry));
H
Haojun Liao 已提交
636

H
Haojun Liao 已提交
637
  for (int32_t i = 0; i < inc; ++i) {
S
Shengliang Guan 已提交
638
    pHashObj->hashList[i + pHashObj->capacity] = (void *)((char *)p + i * sizeof(SHashEntry));
H
Haojun Liao 已提交
639 640 641 642
  }

  taosArrayPush(pHashObj->pMemBlock, &p);

H
Haojun Liao 已提交
643 644 645 646 647 648
  pHashObj->capacity = newCapacity;
  for (int32_t idx = 0; idx < pHashObj->capacity; ++idx) {
    SHashEntry *pe = pHashObj->hashList[idx];
    SHashNode *pNode;
    SHashNode *pNext;
    SHashNode *pPrev = NULL;
H
Haojun Liao 已提交
649

H
Haojun Liao 已提交
650
    if (pe->num == 0) {
H
Haojun Liao 已提交
651
      assert(pe->next == NULL);
H
Haojun Liao 已提交
652
      continue;
H
hjLiao 已提交
653
    }
H
Haojun Liao 已提交
654

H
Haojun Liao 已提交
655
    pNode = pe->next;
H
Haojun Liao 已提交
656

H
Haojun Liao 已提交
657
    assert(pNode != NULL);
H
Haojun Liao 已提交
658

H
Haojun Liao 已提交
659 660 661 662 663 664 665
    while (pNode != NULL) {
      int32_t newIdx = HASH_INDEX(pNode->hashVal, pHashObj->capacity);
      pNext = pNode->next;
      if (newIdx != idx) {
        pe->num -= 1;
        if (pPrev == NULL) {
          pe->next = pNext;
H
Haojun Liao 已提交
666
        } else {
H
Haojun Liao 已提交
667
          pPrev->next = pNext;
H
Haojun Liao 已提交
668
        }
H
Haojun Liao 已提交
669

H
Haojun Liao 已提交
670 671
        SHashEntry *pNewEntry = pHashObj->hashList[newIdx];
        pushfrontNodeInEntryList(pNewEntry, pNode);
H
Haojun Liao 已提交
672
      } else {
H
Haojun Liao 已提交
673
        pPrev = pNode;
H
Haojun Liao 已提交
674
      }
H
Haojun Liao 已提交
675
      pNode = pNext;
H
hjLiao 已提交
676 677 678
    }
  }

H
Haojun Liao 已提交
679 680
  int64_t et = taosGetTimestampUs();

H
Haojun Liao 已提交
681 682
//  uDebug("hash table resize completed, new capacity:%d, load factor:%f, elapsed time:%fms", (int32_t)pHashObj->capacity,
//         ((double)pHashObj->size) / pHashObj->capacity, (et - st) / 1000.0);
H
hjLiao 已提交
683 684 685
}

SHashNode *doCreateHashNode(const void *key, size_t keyLen, const void *pData, size_t dsize, uint32_t hashVal) {
686
  SHashNode *pNewNode = malloc(sizeof(SHashNode) + keyLen + dsize);
H
Haojun Liao 已提交
687

H
hjLiao 已提交
688
  if (pNewNode == NULL) {
H
Haojun Liao 已提交
689
    terrno = TSDB_CODE_OUT_OF_MEMORY;
H
hjLiao 已提交
690 691
    return NULL;
  }
H
Haojun Liao 已提交
692

H
Haojun Liao 已提交
693
  pNewNode->keyLen  = (uint32_t)keyLen;
H
hjLiao 已提交
694
  pNewNode->hashVal = hashVal;
S
hash  
Shengliang Guan 已提交
695
  pNewNode->dataLen = (uint32_t)dsize;
H
Haojun Liao 已提交
696
  pNewNode->refCount= 1;
697
  pNewNode->removed = 0;
H
Haojun Liao 已提交
698
  pNewNode->next    = NULL;
H
Haojun Liao 已提交
699 700 701 702

  memcpy(GET_HASH_NODE_DATA(pNewNode), pData, dsize);
  memcpy(GET_HASH_NODE_KEY(pNewNode), key, keyLen);

H
hjLiao 已提交
703 704 705
  return pNewNode;
}

H
Haojun Liao 已提交
706
void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode) {
H
Haojun Liao 已提交
707
  assert(pNode != NULL && pEntry != NULL);
H
Haojun Liao 已提交
708

H
Haojun Liao 已提交
709 710
  pNode->next = pEntry->next;
  pEntry->next = pNode;
H
Haojun Liao 已提交
711 712

  pEntry->num += 1;
H
hjLiao 已提交
713 714
}

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
715 716 717 718
size_t taosHashGetMemSize(const SHashObj *pHashObj) {
  if (pHashObj == NULL) {
    return 0;
  }
H
Haojun Liao 已提交
719

H
Haojun Liao 已提交
720
  return (pHashObj->capacity * (sizeof(SHashEntry) + sizeof(void*))) + sizeof(SHashNode) * taosHashGetSize(pHashObj) + sizeof(SHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
721
}
H
Haojun Liao 已提交
722

H
Haojun Liao 已提交
723 724 725
void *taosHashGetKey(void *data, size_t* keyLen) {
  SHashNode * node = GET_HASH_PNODE(data);
  if (keyLen != NULL) {
L
Liu Jicong 已提交
726 727
    *keyLen = node->keyLen;
  }
D
dapan1121 已提交
728

H
Haojun Liao 已提交
729
  return GET_HASH_NODE_KEY(node);
W
wpan 已提交
730 731
}

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
732
// release the pNode, return next pNode, and lock the current entry
H
Haojun Liao 已提交
733
static void *taosHashReleaseNode(SHashObj *pHashObj, void *p, int *slot) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
734 735 736 737 738 739
  SHashNode *pOld = (SHashNode *)GET_HASH_PNODE(p);
  SHashNode *prevNode = NULL;

  *slot = HASH_INDEX(pOld->hashVal, pHashObj->capacity);
  SHashEntry *pe = pHashObj->hashList[*slot];

H
Haojun Liao 已提交
740
  taosHashEntryWLock(pHashObj, pe);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
741 742 743

  SHashNode *pNode = pe->next;
  while (pNode) {
H
Haojun Liao 已提交
744 745
    if (pNode == pOld)
      break;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
746 747 748 749 750

    prevNode = pNode;
    pNode = pNode->next;
  }

S
hash  
Shengliang Guan 已提交
751
  if (pNode) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
752
    pNode = pNode->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
753 754 755 756 757
    while (pNode) {
      if (pNode->removed == 0) break;
      pNode = pNode->next;
    }

wafwerar's avatar
wafwerar 已提交
758
    atomic_sub_fetch_16(&pOld->refCount, 1);
H
Haojun Liao 已提交
759
    if (pOld->refCount <=0) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
760 761 762 763 764
      if (prevNode) {
        prevNode->next = pOld->next;
      } else {
        pe->next = pOld->next;
      }
S
hash  
Shengliang Guan 已提交
765

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
766
      pe->num--;
H
Haojun Liao 已提交
767 768
      atomic_sub_fetch_64(&pHashObj->size, 1);
      FREE_HASH_NODE(pOld);
S
hash  
Shengliang Guan 已提交
769
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
770
  } else {
H
Haojun Liao 已提交
771
//    uError("pNode:%p data:%p is not there!!!", pNode, p);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
772 773 774 775 776 777
  }

  return pNode;
}

void *taosHashIterate(SHashObj *pHashObj, void *p) {
S
hash  
Shengliang Guan 已提交
778
  if (pHashObj == NULL) return NULL;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
779

H
Haojun Liao 已提交
780
  int  slot = 0;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
781 782 783
  char *data = NULL;

  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
784
  taosHashRLock(pHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
785 786 787 788 789 790

  SHashNode *pNode = NULL;
  if (p) {
    pNode = taosHashReleaseNode(pHashObj, p, &slot);
    if (pNode == NULL) {
      SHashEntry *pe = pHashObj->hashList[slot];
H
Haojun Liao 已提交
791
      taosHashEntryWUnlock(pHashObj, pe);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
792 793

      slot = slot + 1;
H
Haojun Liao 已提交
794
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
795
  }
H
Haojun Liao 已提交
796

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
797 798 799
  if (pNode == NULL) {
    for (; slot < pHashObj->capacity; ++slot) {
      SHashEntry *pe = pHashObj->hashList[slot];
H
Haojun Liao 已提交
800

H
Haojun Liao 已提交
801
      taosHashEntryWLock(pHashObj, pe);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
802 803

      pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
804 805 806 807 808
      while (pNode) {
        if (pNode->removed == 0) break;
        pNode = pNode->next;
      }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
809 810
      if (pNode) break;

H
Haojun Liao 已提交
811
      taosHashEntryWUnlock(pHashObj, pe);
H
Haojun Liao 已提交
812
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
813
  }
H
Haojun Liao 已提交
814

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
815 816
  if (pNode) {
    SHashEntry *pe = pHashObj->hashList[slot];
817

H
Haojun Liao 已提交
818 819 820
    uint16_t prevRef = atomic_load_16(&pNode->refCount);
    uint16_t afterRef = atomic_add_fetch_16(&pNode->refCount, 1);
    ASSERT(prevRef < afterRef);
821 822 823 824 825

    // the reference count value is overflow, which will cause the delete node operation immediately.
    if (prevRef > afterRef) {
      uError("hash entry ref count overflow, prev ref:%d, current ref:%d", prevRef, afterRef);
      // restore the value
H
Haojun Liao 已提交
826
      atomic_sub_fetch_16(&pNode->refCount, 1);
827 828 829 830 831 832 833 834 835
      data = NULL;
    } else {
      data = GET_HASH_NODE_DATA(pNode);
    }

    if (afterRef >= MAX_WARNING_REF_COUNT) {
      uWarn("hash entry ref count is abnormally high: %d", afterRef);
    }

H
Haojun Liao 已提交
836
    taosHashEntryWUnlock(pHashObj, pe);
H
hjLiao 已提交
837
  }
H
Haojun Liao 已提交
838

H
Haojun Liao 已提交
839
  taosHashRUnlock(pHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
840
  return data;
H
hjLiao 已提交
841
}
H
Haojun Liao 已提交
842

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
843 844
void taosHashCancelIterate(SHashObj *pHashObj, void *p) {
  if (pHashObj == NULL || p == NULL) return;
H
Haojun Liao 已提交
845

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
846
  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
847
  taosHashRLock(pHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
848

H
Haojun Liao 已提交
849
  int slot;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
850 851 852
  taosHashReleaseNode(pHashObj, p, &slot);

  SHashEntry *pe = pHashObj->hashList[slot];
D
dapan1121 已提交
853

H
Haojun Liao 已提交
854 855 856 857
  taosHashEntryWUnlock(pHashObj, pe);
  taosHashRUnlock(pHashObj);
}

858
//TODO remove it
H
Haojun Liao 已提交
859 860 861
void *taosHashAcquire(SHashObj *pHashObj, const void *key, size_t keyLen) {
  void* p = NULL;
  return taosHashGetImpl(pHashObj, key, keyLen, &p, 0, true);
D
dapan1121 已提交
862 863
}

S
hash  
Shengliang Guan 已提交
864
void taosHashRelease(SHashObj *pHashObj, void *p) { taosHashCancelIterate(pHashObj, p); }