static void *assoc_maintenance_thread(void *arg) { mutex_lock(&maintenance_lock); while (do_run_maintenance_thread) { int ii = 0; /* There is only one expansion thread, so no need to global lock. */ for (ii = 0; ii < hash_bulk_move && expanding; ++ii) { item *it, *next; int bucket; void *item_lock = NULL; /* bucket = hv & hashmask(hashpower) =>the bucket of hash table * is the lowest N bits of the hv, and the bucket of item_locks is * also the lowest M bits of hv, and N is greater than M. * So we can process expanding with only one item_lock. cool! */ if ((item_lock = item_trylock(expand_bucket))) { for (it = old_hashtable[expand_bucket]; NULL != it; it = next) { next = it->h_next; bucket = hash(ITEM_key(it), it->nkey) & hashmask(hashpower); it->h_next = primary_hashtable[bucket]; primary_hashtable[bucket] = it; } old_hashtable[expand_bucket] = NULL; expand_bucket++; if (expand_bucket == hashsize(hashpower - 1)) { expanding = false; free(old_hashtable); STATS_LOCK(); stats.hash_bytes -= hashsize(hashpower - 1) * sizeof(void *); stats.hash_is_expanding = 0; STATS_UNLOCK(); if (settings.verbose > 1) fprintf(stderr, "Hash table expansion done\n"); } } else { usleep(10*1000); } if (item_lock) { item_trylock_unlock(item_lock); item_lock = NULL; } } if (!expanding) { /* We are done expanding.. just wait for next invocation */ started_expanding = false; pthread_cond_wait(&maintenance_cond, &maintenance_lock); /* assoc_expand() swaps out the hash table entirely, so we need * all threads to not hold any references related to the hash * table while this happens. * This is instead of a more complex, possibly slower algorithm to * allow dynamic hash table expansion without causing significant * wait times. */ pause_threads(PAUSE_ALL_THREADS); assoc_expand(); pause_threads(RESUME_ALL_THREADS); } } return NULL; }
/* 扩容线程函数,扩容策略如下: * 扩容线程在main函数中创建,在assoc_insert后发现item数目大于哈希表容量1.5倍,唤醒扩容线程。 * 扩容线程先创建一个2倍容量的新哈希表,然后进行把数据从旧哈希表迁移到新哈希表。 * 迁移从旧表索引0开始,每次迁移一个桶(可以增加迁移粒度,但由于迁移需要加锁,可能导致work线程获取锁的等待时间增加), * 迁移完成后释放旧表。 */ static void *assoc_maintenance_thread(void *arg) { mutex_lock(&maintenance_lock); while (do_run_maintenance_thread) { int ii = 0; /* There is only one expansion thread, so no need to global lock. */ //如果expanding为true才会进入循环体,所以迁移线程刚创建的时候,并不会进入循环体 for (ii = 0; ii < hash_bulk_move && expanding; ++ii) { item *it, *next; int bucket; void *item_lock = NULL; /* bucket = hv & hashmask(hashpower) =>the bucket of hash table * is the lowest N bits of the hv, and the bucket of item_locks is * also the lowest M bits of hv, and N is greater than M. * So we can process expanding with only one item_lock. cool! */ //获取单个桶锁 if ((item_lock = item_trylock(expand_bucket))) { //迁移一个桶中所有item for (it = old_hashtable[expand_bucket]; NULL != it; it = next) { next = it->h_next; //重新计算哈希值 bucket = hash(ITEM_key(it), it->nkey) & hashmask(hashpower); it->h_next = primary_hashtable[bucket]; primary_hashtable[bucket] = it; } old_hashtable[expand_bucket] = NULL; expand_bucket++; //迁移完成 if (expand_bucket == hashsize(hashpower - 1)) { expanding = false; //将迁移标志设置0 free(old_hashtable); //释放旧表 STATS_LOCK(); stats.hash_bytes -= hashsize(hashpower - 1) * sizeof(void *); stats.hash_is_expanding = 0; STATS_UNLOCK(); if (settings.verbose > 1) fprintf(stderr, "Hash table expansion done\n"); } } else { usleep(10*1000); } if (item_lock) { item_trylock_unlock(item_lock); item_lock = NULL; } } if (!expanding) { /* We are done expanding.. just wait for next invocation */ //不需要迁移,挂起迁移线程,直到worker线程插入数据后发现item数量已经到了1.5倍哈希表大小, //此时调用worker线程调用assoc_start_expand函数,该函数会调用pthread_cond_signal //唤醒迁移线程 started_expanding = false; pthread_cond_wait(&maintenance_cond, &maintenance_lock); /* assoc_expand() swaps out the hash table entirely, so we need * all threads to not hold any references related to the hash * table while this happens. * This is instead of a more complex, possibly slower algorithm to * allow dynamic hash table expansion without causing significant * wait times. */ pause_threads(PAUSE_ALL_THREADS); assoc_expand(); //申请更大的哈希表,并将expanding设置为true pause_threads(RESUME_ALL_THREADS); } } return NULL; }
void *admin_thread(void *UnusedArg) { SetNameFunction("admin_thr"); while(1) { P(mutex_admin_condvar); while(reload_exports == FALSE) pthread_cond_wait(&(admin_condvar), &(mutex_admin_condvar)); reload_exports = FALSE; V(mutex_admin_condvar); if (rebuild_export_list() <= 0) { LogCrit(COMPONENT_MAIN, "Could not reload the exports list."); continue; } if(pause_threads(PAUSE_RELOAD_EXPORTS) == PAUSE_EXIT) { LogDebug(COMPONENT_MAIN, "Export reload interrupted by shutdown while pausing threads"); /* Be helpfull and exit * (other termination will just blow us away, and that's ok... */ break; } /* Clear the id mapping cache for gss principals to uid/gid. * The id mapping may have changed. */ #ifdef _HAVE_GSSAPI #ifdef _USE_NFSIDMAP uidgidmap_clear(); idmap_clear(); namemap_clear(); #endif /* _USE_NFSIDMAP */ #endif /* _HAVE_GSSAPI */ if (ChangeoverExports()) { LogCrit(COMPONENT_MAIN, "ChangeoverExports failed."); continue; } LogEvent(COMPONENT_MAIN, "Exports reloaded and active"); /* wake_workers could return PAUSE_PAUSE, but we don't have to do * anything special in that case. */ if(wake_threads(AWAKEN_RELOAD_EXPORTS) == PAUSE_EXIT) { LogDebug(COMPONENT_MAIN, "Export reload interrupted by shutdown while waking threads"); /* Be helpfull and exit * (other termination will just blow us away, and that's ok... */ break; } } return NULL; } /* admin_thread */
static void *assoc_maintenance_thread(void *arg) { mutex_lock(&maintenance_lock); //do_run_maintenance_thread是全局变量,初始值为1,在stop_assoc_maintenance_thread //函数中会被赋值0,终止迁移线程 while (do_run_maintenance_thread) { int ii = 0; /* There is only one expansion thread, so no need to global lock. */ for (ii = 0; ii < hash_bulk_move && expanding; ++ii) { item *it, *next; int bucket; void *item_lock = NULL; /* bucket = hv & hashmask(hashpower) =>the bucket of hash table * is the lowest N bits of the hv, and the bucket of item_locks is * also the lowest M bits of hv, and N is greater than M. * So we can process expanding with only one item_lock. cool! */ //hash_bulk_move用来控制每次迁移,移动多少个桶的item。默认是一个. //如果expanding为true才会进入循环体,所以迁移线程刚创建的时候,并不会进入循环体 if ((item_lock = item_trylock(expand_bucket))) { //在assoc_expand函数中expand_bucket会被赋值0 //遍历旧哈希表中由expand_bucket指明的桶,将该桶的所有item //迁移到新哈希表中。 for (it = old_hashtable[expand_bucket]; NULL != it; it = next) { next = it->h_next; //重新计算新的哈希值,得到其在新哈希表的位置 bucket = hash(ITEM_key(it), it->nkey) & hashmask(hashpower); //将这个item插入到新哈希表中 it->h_next = primary_hashtable[bucket]; primary_hashtable[bucket] = it; } //不需要清空旧桶。直接将冲突链的链头赋值为NULL即可 old_hashtable[expand_bucket] = NULL; //迁移完一个桶,接着把expand_bucket指向下一个待迁移的桶 expand_bucket++; if (expand_bucket == hashsize(hashpower - 1)) {//全部数据迁移完毕 expanding = false;//将扩展标志设置为false free(old_hashtable); STATS_LOCK(); stats.hash_bytes -= hashsize(hashpower - 1) * sizeof(void *); stats.hash_is_expanding = 0; STATS_UNLOCK(); if (settings.verbose > 1) fprintf(stderr, "Hash table expansion done\n"); } } else { usleep(10*1000); } if (item_lock) { //遍历完hash_bulk_move个桶的所有item后,就释放锁 item_trylock_unlock(item_lock); item_lock = NULL; } } if (!expanding) {//不需要迁移数据(了) /* We are done expanding.. just wait for next invocation */ started_expanding = false;//重置 //挂起迁移线程,直到worker线程插入数据后发现item数量已经到了1.5倍哈希表大小, //此时调用worker线程调用assoc_start_expand函数,该函数会调用pthread_cond_signal //唤醒迁移线程 pthread_cond_wait(&maintenance_cond, &maintenance_lock); /* assoc_expand() swaps out the hash table entirely, so we need * all threads to not hold any references related to the hash * table while this happens. * This is instead of a more complex, possibly slower algorithm to * allow dynamic hash table expansion without causing significant * wait times. */ pause_threads(PAUSE_ALL_THREADS); assoc_expand();//申请更大的哈希表,并将expanding设置为true pause_threads(RESUME_ALL_THREADS); } } return NULL; }