/** * Purge all non-complete (i.e. not 'resdone') entries and invoke their * callback, setting the result's error code with the specified error */ static void purge_entries(lcb_durability_set_t *dset, lcb_error_t err) { lcb_size_t ii; dset->us_timeout = 0; dset->next_state = STATE_IGNORE; /** * Each time we call 'ent_set_resdone' we might cause the refcount to drop * to zero, making 'dset' point to freed memory. To avoid this, we bump * up the refcount before the loop and defer the possible free operation * until the end. */ dset_ref(dset); for (ii = 0; ii < dset->nentries; ii++) { lcb_durability_entry_t *ent = dset->entries + ii; if (ent->done) { continue; } RESFLD(ent, err) = err; ent_set_resdone(ent); } dset_unref(dset); }
static lcb_error_t dset_ctx_schedule(lcb_MULTICMD_CTX *mctx, const void *cookie) { unsigned ii; char *kptr; lcb_DURSET *dset = CTX_FROM_MULTI(mctx); kptr = dset->kvbufs.base; for (ii = 0; ii < dset->nentries; ii++) { lcb_DURITEM *ent = dset->entries + ii; RESFLD(ent, key) = kptr; kptr += RESFLD(ent, nkey); if (ent->hashkey.contig.nbytes) { ent->hashkey.contig.bytes = kptr; kptr += ent->hashkey.contig.nbytes; } if (dset->ht) { int mt = genhash_update(dset->ht, RESFLD(ent, key), RESFLD(ent, nkey), ent, 0); if (mt != NEW) { lcb_durability_dset_destroy(dset); return LCB_DUPLICATE_COMMANDS; } } } dset_ref(dset); dset->cookie = cookie; dset->nremaining = dset->nentries; lcb_aspend_add(&dset->instance->pendops, LCB_PENDTYPE_DURABILITY, dset); return poll_once(dset, 1); }
LIBCOUCHBASE_API lcb_error_t lcb_durability_poll(lcb_t instance, const void *cookie, const lcb_durability_opts_t *options, lcb_size_t ncmds, const lcb_durability_cmd_t *const *cmds) { hrtime_t now = gethrtime(); lcb_durability_set_t *dset; lcb_size_t ii; lcb_io_opt_t io = instance->settings.io; if (!ncmds) { return LCB_EINVAL; } dset = calloc(1, sizeof(*dset)); if (!dset) { return LCB_CLIENT_ENOMEM; } dset->opts = *options; dset->instance = instance; if (!DSET_OPTFLD(dset, timeout)) { DSET_OPTFLD(dset, timeout) = instance->settings.durability_timeout; } if (-1 == verify_critera(instance, dset)) { free(dset); return LCB_DURABILITY_ETOOMANY; } /* set our timeouts now */ dset->us_timeout = (lcb_uint32_t)(now / 1000) + DSET_OPTFLD(dset, timeout); dset->timer = io->v.v0.create_timer(io); dset->cookie = cookie; dset->nentries = ncmds; dset->nremaining = ncmds; /** Get the timings */ if (!DSET_OPTFLD(dset, interval)) { DSET_OPTFLD(dset, interval) = LCB_DEFAULT_DURABILITY_INTERVAL; } /* list of observe commands to schedule */ if (dset->nentries == 1) { dset->entries = &dset->single.ent; dset->valid_entries = &dset->single.entp; } else { dset->ht = lcb_hashtable_nc_new(dset->nentries); dset->entries = calloc(dset->nentries, sizeof(*dset->entries)); dset->valid_entries = malloc(dset->nentries * sizeof(*dset->valid_entries)); if (dset->entries == NULL || dset->valid_entries == NULL) { lcb_durability_dset_destroy(dset); return LCB_CLIENT_ENOMEM; } } /* set up the observe commands */ for (ii = 0; ii < dset->nentries; ii++) { lcb_durability_entry_t *ent = dset->entries + ii; ent_init(cmds[ii], ent); ent->parent = dset; if (dset->nentries > 1) { int mt = genhash_update(dset->ht, REQFLD(ent, key), REQFLD(ent, nkey), ent, 0); if (mt != NEW) { lcb_durability_dset_destroy(dset); return LCB_DUPLICATE_COMMANDS; } } } /** * Increase the refcount by one. This will be decremented * when the remaining_total count hits 0 */ dset_ref(dset); hashset_add(instance->durability_polls, dset); timer_schedule(dset, 0, STATE_OBSPOLL); return lcb_synchandler_return(instance, LCB_SUCCESS); }
/** * Schedules a single sweep of observe requests. */ static void poll_once(lcb_durability_set_t *dset) { lcb_size_t ii, oix; lcb_error_t err; /** * We should never be called while an 'iter' operation is still * in progress */ lcb_assert(dset->waiting == 0); dset_ref(dset); for (ii = 0, oix = 0; ii < dset->nentries; ii++) { struct lcb_durability_entry_st *ent = dset->entries + ii; if (ent->done) { continue; } /* reset all the per-iteration fields */ RESFLD(ent, persisted_master) = 0; RESFLD(ent, exists_master) = 0; RESFLD(ent, npersisted) = 0; RESFLD(ent, nreplicated) = 0; RESFLD(ent, cas) = 0; RESFLD(ent, err) = LCB_SUCCESS; dset->valid_entries[oix++] = ent; } lcb_assert(oix == dset->nremaining); err = lcb_observe_ex(dset->instance, dset, dset->nremaining, (const void * const *)dset->valid_entries, LCB_OBSERVE_TYPE_DURABILITY); if (err != LCB_SUCCESS) { for (ii = 0; ii < dset->nentries; ii++) { lcb_durability_entry_t *ent = dset->entries + ii; if (ent->done) { continue; } RESFLD(ent, err) = err; ent_set_resdone(ent); } } else { dset->waiting = 1; dset_ref(dset); } if (dset->waiting && oix) { lcb_uint32_t us_now = (lcb_uint32_t)(gethrtime() / 1000); lcb_uint32_t us_tmo; if (dset->us_timeout > us_now) { us_tmo = dset->us_timeout - us_now; } else { us_tmo = 1; } timer_schedule(dset, us_tmo, STATE_TIMEOUT); } else { purge_entries(dset, LCB_ERROR); } dset_unref(dset); }
/** * Schedules a single sweep of observe requests. */ static void poll_once(lcb_DURSET *dset) { unsigned ii, n_added = 0; lcb_error_t err; lcb_MULTICMD_CTX *mctx = NULL; /** * We should never be called while an 'iter' operation is still * in progress */ lcb_assert(dset->waiting == 0); dset_ref(dset); mctx = lcb_observe_ctx_dur_new(dset->instance); if (!mctx) { err = LCB_CLIENT_ENOMEM; goto GT_ERR; } for (ii = 0; ii < dset->nentries; ii++) { lcb_CMDOBSERVE cmd = { 0 }; struct lcb_durability_entry_st *ent = dset->entries + ii; if (ent->done) { continue; } /* reset all the per-iteration fields */ RESFLD(ent, persisted_master) = 0; RESFLD(ent, exists_master) = 0; RESFLD(ent, npersisted) = 0; RESFLD(ent, nreplicated) = 0; RESFLD(ent, cas) = 0; RESFLD(ent, rc) = LCB_SUCCESS; LCB_KREQ_SIMPLE(&cmd.key, RESFLD(ent, key), RESFLD(ent, nkey)); cmd.hashkey = ent->hashkey; err = mctx->addcmd(mctx, (lcb_CMDBASE *)&cmd); if (err != LCB_SUCCESS) { goto GT_ERR; } n_added ++; } lcb_assert(n_added == dset->nremaining); if (n_added) { lcb_sched_enter(dset->instance); mctx->done(mctx, dset); lcb_sched_leave(dset->instance); } GT_ERR: if (err != LCB_SUCCESS) { if (mctx) { mctx->fail(mctx); } for (ii = 0; ii < dset->nentries; ii++) { lcb_DURITEM *ent = dset->entries + ii; if (ent->done) { continue; } RESFLD(ent, rc) = err; ent_set_resdone(ent); } } else { dset->waiting = 1; dset_ref(dset); } if (dset->waiting && n_added) { lcb_uint32_t us_now = (lcb_uint32_t)(gethrtime() / 1000); lcb_uint32_t us_tmo; if (dset->us_timeout > us_now) { us_tmo = dset->us_timeout - us_now; } else { us_tmo = 1; } timer_schedule(dset, us_tmo, STATE_TIMEOUT); } else { purge_entries(dset, LCB_ERROR); } dset_unref(dset); }