/** * 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); }
/** * Called when the last (primitive) OBSERVE response is received for the entry. */ static void dset_done_waiting(lcb_durability_set_t *dset) { lcb_assert(dset->waiting || ("Got NULL callback twice!" && 0)); dset->waiting = 0; if (dset->nremaining > 0) { timer_schedule(dset, DSET_OPTFLD(dset, interval), STATE_OBSPOLL); } dset_unref(dset); }
/** * Set the logical state of the entry to done, and invoke the callback. * It is safe to call this multiple times */ static void ent_set_resdone(lcb_DURITEM *ent) { lcb_RESPCALLBACK callback; if (ent->done) { return; } ent->done = 1; ent->parent->nremaining--; /** Invoke the callback now :) */ ent->result.cookie = (void *)ent->parent->cookie; callback = lcb_find_callback(ent->parent->instance, LCB_CALLBACK_ENDURE); callback(ent->parent->instance, LCB_CALLBACK_ENDURE, (lcb_RESPBASE*)&ent->result); if (ent->parent->nremaining == 0) { dset_unref(ent->parent); } }
/** * Set the logical state of the entry to done, and invoke the callback. * It is safe to call this multiple times */ static void ent_set_resdone(lcb_durability_entry_t *ent) { if (ent->done) { return; } ent->done = 1; ent->parent->nremaining--; /** * Invoke the callback now :) */ ent->parent->instance->callbacks.durability(ent->parent->instance, ent->parent->cookie, LCB_SUCCESS, &ent->result); if (ent->parent->nremaining == 0) { dset_unref(ent->parent); } }
/** * 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); }