/* * pmemobj_root_construct -- returns root object */ PMEMoid pmemobj_root_construct(PMEMobjpool *pop, size_t size, void (*constructor)(PMEMobjpool *pop, void *ptr, void *arg), void *arg) { LOG(3, "pop %p size %zu constructor %p args %p", pop, size, constructor, arg); if (size > PMEMOBJ_MAX_ALLOC_SIZE) { ERR("requested size too large"); errno = ENOMEM; return OID_NULL; } PMEMoid root; pmemobj_mutex_lock(pop, &pop->rootlock); if (pop->store->root.head.pe_first.off == 0) /* root object list is empty */ obj_alloc_root(pop, pop->store, size, constructor, arg); else { size_t old_size = pmemobj_root_size(pop); if (size > old_size) if (obj_realloc_root(pop, pop->store, size, old_size, constructor, arg)) { pmemobj_mutex_unlock(pop, &pop->rootlock); LOG(2, "obj_realloc_root failed"); return OID_NULL; } } root = pop->store->root.head.pe_first; pmemobj_mutex_unlock(pop, &pop->rootlock); return root; }
/** * Unlocks a previously locked mutex. * * Unlocking a mutex that has not been locked by the current * thread results in undefined behavior. Unlocking a mutex that * has not been lock also results in undefined behavior. * * @throw lock_error when an error occurs, this includes all * system related errors with the underlying implementation of * the mutex. */ void unlock() { PMEMobjpool *pop = pmemobj_pool_by_ptr(this); if (int ret = pmemobj_mutex_unlock(pop, &this->plock)) throw lock_error(ret, std::system_category(), "Failed to unlock a mutex."); }
/* * list_mutexes_lock -- (internal) grab one or two locks in ascending * address order */ static inline int list_mutexes_lock(PMEMobjpool *pop, struct list_head *head1, struct list_head *head2) { ASSERTne(head1, NULL); if (!head2 || head1 == head2) return pmemobj_mutex_lock(pop, &head1->lock); PMEMmutex *lock1; PMEMmutex *lock2; if ((uintptr_t)&head1->lock < (uintptr_t)&head2->lock) { lock1 = &head1->lock; lock2 = &head2->lock; } else { lock1 = &head2->lock; lock2 = &head1->lock; } int ret; if ((ret = pmemobj_mutex_lock(pop, lock1))) goto err; if ((ret = pmemobj_mutex_lock(pop, lock2))) goto err_unlock; return 0; err_unlock: pmemobj_mutex_unlock(pop, lock1); err: return ret; }
/* * timed_check_worker -- (internal) check consistency with mutex */ static void * timed_check_worker(void *arg) { for (unsigned run = 0; run < WORKER_RUNS; run++) { int mutex_id = (int)(uintptr_t)arg % 2; PMEMmutex *mtx = mutex_id == LOCKED_MUTEX ? &Test_obj->mutex_locked : &Test_obj->mutex; struct timespec t1, t2, t_diff, abs_time; os_clock_gettime(CLOCK_REALTIME, &t1); abs_time = t1; abs_time.tv_nsec += TIMEOUT; if (abs_time.tv_nsec >= NANO_PER_ONE) { abs_time.tv_sec += abs_time.tv_nsec / NANO_PER_ONE; abs_time.tv_nsec %= NANO_PER_ONE; } int ret = pmemobj_mutex_timedlock(&Mock_pop, mtx, &abs_time); os_clock_gettime(CLOCK_REALTIME, &t2); if (mutex_id == LOCKED_MUTEX) { UT_ASSERTeq(ret, ETIMEDOUT); t_diff.tv_sec = t2.tv_sec - t1.tv_sec; t_diff.tv_nsec = t2.tv_nsec - t1.tv_nsec; if (t_diff.tv_nsec < 0) { --t_diff.tv_sec; t_diff.tv_nsec += NANO_PER_ONE; } UT_ASSERT(t_diff.tv_sec * NANO_PER_ONE + t_diff.tv_nsec >= TIMEOUT); return NULL; } if (ret == 0) { UT_ASSERTne(mutex_id, LOCKED_MUTEX); pmemobj_mutex_unlock(&Mock_pop, mtx); } else if (ret == ETIMEDOUT) { t_diff.tv_sec = t2.tv_sec - t1.tv_sec; t_diff.tv_nsec = t2.tv_nsec - t1.tv_nsec; if (t_diff.tv_nsec < 0) { --t_diff.tv_sec; t_diff.tv_nsec += NANO_PER_ONE; } UT_ASSERT(t_diff.tv_sec * NANO_PER_ONE + t_diff.tv_nsec >= TIMEOUT); } else { errno = ret; UT_ERR("!pmemobj_mutex_timedlock"); } } return NULL; }
/* * mutex_write_worker -- (internal) write data with mutex */ static void * mutex_write_worker(void *arg) { for (unsigned run = 0; run < WORKER_RUNS; run++) { if (pmemobj_mutex_lock(&Mock_pop, &Test_obj->mutex)) { UT_ERR("pmemobj_mutex_lock"); return NULL; } memset(Test_obj->data, (int)(uintptr_t)arg, DATA_SIZE); if (pmemobj_mutex_unlock(&Mock_pop, &Test_obj->mutex)) UT_ERR("pmemobj_mutex_unlock"); } return NULL; }
/* * cond_write_worker -- (internal) write data with cond variable */ static void * cond_write_worker(void *arg) { for (unsigned run = 0; run < WORKER_RUNS; run++) { if (pmemobj_mutex_lock(&Mock_pop, &Test_obj->mutex)) return NULL; memset(Test_obj->data, (int)(uintptr_t)arg, DATA_SIZE); Test_obj->check_data = 1; if (pmemobj_cond_signal(&Mock_pop, &Test_obj->cond)) UT_ERR("pmemobj_cond_signal"); pmemobj_mutex_unlock(&Mock_pop, &Test_obj->mutex); } return NULL; }
/* * mutex_check_worker -- (internal) check consistency with mutex */ static void * mutex_check_worker(void *arg) { for (unsigned run = 0; run < WORKER_RUNS; run++) { if (pmemobj_mutex_lock(&Mock_pop, &Test_obj->mutex)) { UT_ERR("pmemobj_mutex_lock"); return NULL; } uint8_t val = Test_obj->data[0]; for (int i = 1; i < DATA_SIZE; i++) UT_ASSERTeq(Test_obj->data[i], val); memset(Test_obj->data, 0, DATA_SIZE); if (pmemobj_mutex_unlock(&Mock_pop, &Test_obj->mutex)) UT_ERR("pmemobj_mutex_unlock"); } return NULL; }
int main(int argc, char *argv[]) { START(argc, argv, "obj_sync"); util_init(); if (argc < 4) FATAL_USAGE(); worker writer; worker checker; char test_type = argv[1][0]; switch (test_type) { case 'm': writer = mutex_write_worker; checker = mutex_check_worker; break; case 'r': writer = rwlock_write_worker; checker = rwlock_check_worker; break; case 'c': writer = cond_write_worker; checker = cond_check_worker; break; case 't': writer = timed_write_worker; checker = timed_check_worker; break; default: FATAL_USAGE(); } unsigned long num_threads = strtoul(argv[2], NULL, 10); if (num_threads > MAX_THREAD_NUM) UT_FATAL("Do not use more than %d threads.\n", MAX_THREAD_NUM); unsigned long opens = strtoul(argv[3], NULL, 10); if (opens > MAX_OPENS) UT_FATAL("Do not use more than %d runs.\n", MAX_OPENS); os_thread_t *write_threads = (os_thread_t *)MALLOC(num_threads * sizeof(os_thread_t)); os_thread_t *check_threads = (os_thread_t *)MALLOC(num_threads * sizeof(os_thread_t)); /* first pool open */ mock_open_pool(&Mock_pop); Mock_pop.p_ops.persist = obj_sync_persist; Mock_pop.p_ops.base = &Mock_pop; Test_obj = (struct mock_obj *)MALLOC(sizeof(struct mock_obj)); /* zero-initialize the test object */ pmemobj_mutex_zero(&Mock_pop, &Test_obj->mutex); pmemobj_mutex_zero(&Mock_pop, &Test_obj->mutex_locked); pmemobj_cond_zero(&Mock_pop, &Test_obj->cond); pmemobj_rwlock_zero(&Mock_pop, &Test_obj->rwlock); Test_obj->check_data = 0; memset(&Test_obj->data, 0, DATA_SIZE); for (unsigned long run = 0; run < opens; run++) { if (test_type == 't') { pmemobj_mutex_lock(&Mock_pop, &Test_obj->mutex_locked); } for (unsigned i = 0; i < num_threads; i++) { PTHREAD_CREATE(&write_threads[i], NULL, writer, (void *)(uintptr_t)i); PTHREAD_CREATE(&check_threads[i], NULL, checker, (void *)(uintptr_t)i); } for (unsigned i = 0; i < num_threads; i++) { PTHREAD_JOIN(&write_threads[i], NULL); PTHREAD_JOIN(&check_threads[i], NULL); } if (test_type == 't') { pmemobj_mutex_unlock(&Mock_pop, &Test_obj->mutex_locked); } /* up the run_id counter and cleanup */ mock_open_pool(&Mock_pop); cleanup(test_type); } FREE(check_threads); FREE(write_threads); FREE(Test_obj); DONE(NULL); }