306
306
/* A segment that also contains a segment table */
308
308
struct segment s; /* The segment itself. Must be first */
310
struct segment** last_segtab; /* Used if table is shrinking */
311
struct segment* segtab[1]; /* The segment table (may be larger) */
310
struct segment** prev_segtab; /* Used when table is shrinking */
311
int nsegs; /* Size of segtab */
312
struct segment* segtab[1]; /* The segment table */
314
#define SIZEOF_EXTSEG(NSEGS) \
315
(sizeof(struct ext_segment) - sizeof(struct segment*) + sizeof(struct segment*)*(NSEGS))
318
# include <stddef.h> /* offsetof */
319
# define EXTSEG(SEGTAB_PTR) \
320
((struct ext_segment*) (((char*)(SEGTAB_PTR)) - offsetof(struct ext_segment,segtab)))
324
/* How the table segments relate to each other:
326
ext_segment: ext_segment: "plain" segment
327
#=================# #================# #=============#
328
| bucket[0] |<--+ +------->| bucket[256] | +->| bucket[512] |
329
| bucket[1] | | | | [257] | | | [513] |
331
| bucket[255] | | | | [511] | | | [767] |
332
|-----------------| | | |----------------| | #=============#
333
| prev_segtab=NULL| | | +--<---prev_segtab | |
334
| nsegs = 2 | | | | | nsegs = 256 | |
335
+->| segtab[0] -->-------+---|---|--<---segtab[0] |<-+ |
336
| | segtab[1] -->-----------+---|--<---segtab[1] | | |
337
| #=================# | | segtab[2] -->-----|--+ ext_segment:
338
| | : : | #================#
339
+----------------<---------------+ | segtab[255] ->----|----->| bucket[255*256]|
340
#================# | | |
343
+----<---prev_segtab |
315
349
** Forward decl's (static functions)
351
static struct ext_segment* alloc_ext_seg(DbTableHash* tb, unsigned seg_ix,
352
struct segment** old_segtab);
317
353
static int alloc_seg(DbTableHash *tb);
318
354
static int free_seg(DbTableHash *tb, int free_records);
319
355
static HashDbTerm* next(DbTableHash *tb, Uint *iptr, erts_smp_rwmtx_t** lck_ptr,
576
612
int db_create_hash(Process *p, DbTable *tbl)
578
614
DbTableHash *tb = &tbl->hash;
579
616
erts_smp_atomic_init(&tb->szm, SEGSZ_MASK);
580
617
erts_smp_atomic_init(&tb->nactive, SEGSZ);
581
618
erts_smp_atomic_init(&tb->fixdel, (long)NULL);
582
erts_smp_atomic_init(&tb->segtab, (long)NULL);
619
erts_smp_atomic_init(&tb->segtab, (long) alloc_ext_seg(tb,0,NULL)->segtab);
587
623
erts_smp_atomic_init(&tb->is_resizing, 0);
1427
1462
match_list = NIL;
1430
if (current->hvalue != INVALID_HASH &&
1432
db_prog_match(p,mpi.mp,
1433
make_tuple(current->dbterm.tpl),
1435
is_value(match_res))) {
1436
if (mpi.all_objects) {
1437
hp = HAlloc(p, current->dbterm.size + 2);
1438
match_res = copy_shallow(DBTERM_BUF(¤t->dbterm),
1439
current->dbterm.size,
1443
sz = size_object(match_res);
1445
hp = HAlloc(p, sz + 2);
1446
match_res = copy_struct(match_res, sz, &hp, &MSO(p));
1448
match_list = CONS(hp, match_res, match_list);
1452
if (mpi.key_given) { /* Key is bound */
1453
current = current->next;
1455
while (current != NULL &&
1456
current->hvalue == INVALID_HASH)
1457
current = current->next;
1458
if (current == NULL) {
1460
if (current_list_pos == mpi.num_lists) {
1461
slot_ix = -1; /* EOT */
1465
if (current != NULL) {
1466
if (current->hvalue != INVALID_HASH) {
1467
match_res = db_prog_match(p,mpi.mp,
1468
make_tuple(current->dbterm.tpl),
1470
if (is_value(match_res)) {
1471
if (mpi.all_objects) {
1472
hp = HAlloc(p, current->dbterm.size + 2);
1473
match_res = copy_shallow(DBTERM_BUF(¤t->dbterm),
1474
current->dbterm.size,
1464
slot_ix = mpi.lists[current_list_pos].ix;
1465
lck = RLOCK_HASH(tb, slot_ix);
1466
current = *(mpi.lists[current_list_pos].bucket);
1467
ASSERT(mpi.lists[current_list_pos].bucket == &BUCKET(tb,slot_ix));
1478
sz = size_object(match_res);
1480
hp = HAlloc(p, sz + 2);
1481
match_res = copy_struct(match_res, sz, &hp, &MSO(p));
1483
match_list = CONS(hp, match_res, match_list);
1475
else { /* Key is variable */
1487
current = current->next;
1489
else if (mpi.key_given) { /* Key is bound */
1491
if (current_list_pos == mpi.num_lists) {
1492
slot_ix = -1; /* EOT */
1495
slot_ix = mpi.lists[current_list_pos].ix;
1496
lck = RLOCK_HASH(tb, slot_ix);
1497
current = *(mpi.lists[current_list_pos].bucket);
1498
ASSERT(mpi.lists[current_list_pos].bucket == &BUCKET(tb,slot_ix));
1502
else { /* Key is variable */
1477
save_slot_ix = slot_ix;
1479
next(tb, (Uint*)&slot_ix, &lck, current)) == NULL) {
1505
if ((slot_ix=next_slot(tb,slot_ix,&lck)) == 0) {
1483
if (slot_ix != save_slot_ix) {
1484
if (chunk_size && got >= chunk_size) {
1488
if (num_left <= 0 || MBUF(p)) {
1490
* We have either reached our limit, or just created some heap fragments.
1491
* Since many heap fragments will make the GC slower, trap and GC now.
1509
if (chunk_size && got >= chunk_size) {
1513
if (num_left <= 0 || MBUF(p)) {
1515
* We have either reached our limit, or just created some heap fragments.
1516
* Since many heap fragments will make the GC slower, trap and GC now.
1521
current = BUCKET(tb,slot_ix);
2284
2307
return DB_ERROR_NONE;
2287
/* Extend (or initialize) table with one new segment
2310
static struct ext_segment* alloc_ext_seg(DbTableHash* tb, unsigned seg_ix,
2311
struct segment** old_segtab)
2314
struct ext_segment* eseg;
2317
case 0: nsegs = NSEG_1; break;
2318
case 1: nsegs = NSEG_2; break;
2319
default: nsegs = seg_ix + NSEG_INC; break;
2321
eseg = (struct ext_segment*) erts_db_alloc_fnf(ERTS_ALC_T_DB_SEG,
2323
SIZEOF_EXTSEG(nsegs));
2324
ASSERT(eseg != NULL);
2325
sys_memset(&eseg->s, 0, sizeof(struct segment));
2326
IF_DEBUG(eseg->s.is_ext_segment = 1);
2327
eseg->prev_segtab = old_segtab;
2328
eseg->nsegs = nsegs;
2330
ASSERT(nsegs > tb->nsegs);
2331
sys_memcpy(eseg->segtab, old_segtab, tb->nsegs*sizeof(struct segment*));
2334
sys_memset(&eseg->segtab[seg_ix], 0, (nsegs-seg_ix)*sizeof(struct segment*));
2336
eseg->segtab[seg_ix] = &eseg->s;
2340
/* Extend table with one new segment
2289
2342
static int alloc_seg(DbTableHash *tb)
2291
int six = tb->nslots >> SEGSZ_EXP;
2293
if (six == tb->nsegs) { /* New extended segment */
2296
struct ext_segment* eseg;
2297
struct segment** old_segtab = SEGTAB(tb);
2300
case 0: nsegs = NSEG_1; break;
2301
case 1: nsegs = NSEG_2; break;
2302
default: nsegs = six + NSEG_INC; break;
2305
bytes = sizeof(struct ext_segment) + sizeof(struct segment*) * (nsegs-1);
2306
eseg = (struct ext_segment*) erts_db_alloc_fnf(ERTS_ALC_T_DB_SEG,
2307
(DbTable *) tb, bytes);
2308
if (eseg == NULL) return 0;
2310
memset(&eseg->s, 0, sizeof(struct segment));
2311
IF_DEBUG(eseg->s.is_ext_segment = 1);
2312
eseg->last_segtab = old_segtab;
2314
ASSERT(nsegs > tb->nsegs);
2315
memcpy(eseg->segtab, old_segtab, tb->nsegs*sizeof(struct segment*));
2316
memset(&eseg->segtab[six], 0, (nsegs-six)*sizeof(struct segment*));
2318
eseg->segtab[six] = &eseg->s;
2319
erts_smp_atomic_set(&tb->segtab, (long) eseg->segtab);
2344
int seg_ix = tb->nslots >> SEGSZ_EXP;
2346
if (seg_ix+1 == tb->nsegs) { /* New segtab needed (extended segment) */
2347
struct segment** segtab = SEGTAB(tb);
2348
struct ext_segment* seg = alloc_ext_seg(tb, seg_ix, segtab);
2349
if (seg == NULL) return 0;
2350
segtab[seg_ix] = &seg->s;
2351
/* We don't use the new segtab until next call (see "shrink race") */
2322
2353
else { /* Just a new plain segment */
2323
struct segment** segtab = SEGTAB(tb);
2324
ASSERT(six < tb->nsegs);
2325
segtab[six] = (struct segment*) erts_db_alloc_fnf(ERTS_ALC_T_DB_SEG,
2327
sizeof(struct segment));
2328
if (segtab[six] == NULL) return 0;
2329
memset(segtab[six], 0, sizeof(struct segment));
2354
struct segment** segtab;
2355
if (seg_ix == tb->nsegs) { /* Time to start use segtab from last call */
2356
struct ext_segment* eseg;
2357
eseg = (struct ext_segment*) SEGTAB(tb)[seg_ix-1];
2358
MY_ASSERT(eseg!=NULL && eseg->s.is_ext_segment);
2359
erts_smp_atomic_set(&tb->segtab, (long) eseg->segtab);
2360
tb->nsegs = eseg->nsegs;
2362
ASSERT(seg_ix < tb->nsegs);
2363
segtab = SEGTAB(tb);
2364
ASSERT(segtab[seg_ix] == NULL);
2365
segtab[seg_ix] = (struct segment*) erts_db_alloc_fnf(ERTS_ALC_T_DB_SEG,
2367
sizeof(struct segment));
2368
if (segtab[seg_ix] == NULL) return 0;
2369
sys_memset(segtab[seg_ix], 0, sizeof(struct segment));
2331
2371
tb->nslots += SEGSZ;
2362
if (segtab == top->segtab) { /* Extended segment */
2363
MY_ASSERT(top->s.is_ext_segment);
2364
erts_smp_atomic_set(&tb->segtab, (long)top->last_segtab);
2365
bytes = sizeof(struct ext_segment) + sizeof(struct segment*) * (tb->nsegs-1);
2404
/* The "shrink race":
2405
* We must avoid deallocating an extended segment while its segtab may
2406
* still be used by other threads.
2407
* The trick is to stop use a segtab one call earlier. That is, stop use
2408
* a segtab when the segment above it is deallocated. When the segtab is
2409
* later deallocated, it has not been used for a very long time.
2410
* It is even theoretically safe as we have by then rehashed the entire
2411
* segment, seizing *all* locks, so there cannot exist any retarded threads
2412
* still hanging in BUCKET macro with an old segtab pointer.
2413
* For this to work, we must of course allocate a new segtab one call
2414
* earlier in alloc_seg() as well. And this is also the reason why
2415
* the minimum size of the first segtab is 2 and not 1 (NSEG_1).
2418
if (seg_ix == tb->nsegs-1 || seg_ix==0) { /* Dealloc extended segment */
2419
MY_ASSERT(top->s.is_ext_segment);
2420
ASSERT(segtab != top->segtab || seg_ix==0);
2421
bytes = SIZEOF_EXTSEG(top->nsegs);
2368
else { /* Plain segment */
2423
else { /* Dealloc plain segment */
2424
struct ext_segment* newtop = (struct ext_segment*) segtab[seg_ix-1];
2369
2425
MY_ASSERT(!top->s.is_ext_segment);
2427
if (segtab == newtop->segtab) { /* New top segment is extended */
2428
MY_ASSERT(newtop->s.is_ext_segment);
2429
if (newtop->prev_segtab != NULL) {
2430
/* Time to use a smaller segtab */
2431
erts_smp_atomic_set(&tb->segtab, (long)newtop->prev_segtab);
2433
ASSERT(tb->nsegs == EXTSEG(SEGTAB(tb))->nsegs);
2436
ASSERT(NSEG_1 > 2 && seg_ix==1);
2371
2439
bytes = sizeof(struct segment);
2374
2442
erts_db_free(ERTS_ALC_T_DB_SEG, (DbTable *)tb,
2375
2443
(void*)top, bytes);
2446
if (seg_ix < tb->nsegs) SEGTAB(tb)[seg_ix] = NULL;
2448
erts_smp_atomic_set(&tb->segtab, (long)NULL);
2377
2451
tb->nslots -= SEGSZ;
2378
2452
ASSERT(tb->nslots >= 0);
2379
2453
return nrecords;