void mutex_lock(struct mutex *mutex) { assert(mutex->name); SLOG(LOG_DEBUG, "Locking %s", mutex_name(mutex)); uint64_t const start = bench_event_start(); # ifdef WITH_BENCH int err = pthread_mutex_trylock(&mutex->mutex); switch (err) { case 0: bench_event_fire(&mutex->lock_for_free); break; case EBUSY: err = pthread_mutex_lock(&mutex->mutex); break; default: // other errors (for lock and trylock) handled below break; } # else // This was found to be noticably faster (-1% cpu load) int err = pthread_mutex_lock(&mutex->mutex); # endif if (! err) { bench_event_stop(&mutex->acquiring_lock, start); SLOG(LOG_DEBUG, "Locked %s", mutex_name(mutex)); } else { SLOG(LOG_ERR, "Cannot lock %s: %s", mutex_name(mutex), strerror(err)); // so be it } }
void doomer_run(void) { enter_mono_region(); SLOG(LOG_DEBUG, "Deleting doomed objects..."); unsigned nb_dels = 0, nb_rescued = 0; // Bench time spent scanning death_row uint64_t start = bench_event_start(); // Rescue from death_row the objects which ref count is > 0, // and queue into kill_list the one no longer accessible (they can not even reach each others) struct refs to_kill; SLIST_INIT(&to_kill); struct ref *r; while (NULL != (r = SLIST_FIRST(&death_row))) { SLIST_REMOVE_HEAD(&death_row, entry); if (r->count == 0) { SLIST_INSERT_HEAD(&to_kill, r, entry); nb_dels ++; } else { nb_rescued ++; } } SLOG(nb_dels + nb_rescued > 0 ? LOG_INFO:LOG_DEBUG, "Deleted %u objects, rescued %u", nb_dels, nb_rescued); bench_event_stop(&dooming, start); // No need to block parsing any more since the selected objects are not accessible leave_protected_region(); // Delete all selected objects while (NULL != (r = SLIST_FIRST(&to_kill))) { // Beware that r->del() may doom further objects, which will be added in the death_row for next run SLOG(LOG_DEBUG, "Delete next object on kill list: %p", r); SLIST_REMOVE_HEAD(&to_kill, entry); r->entry.sle_next = NULL; // the deletor must not care about the ref (since the decision to del the object was already taken) r->del(r); } }