// read | write ---------------------------
+void Objecter::op_post_split_op_complete(Op* op, bs::error_code ec, int rc) {
-void Objecter::op_post_submit(Op* op) {
- boost::asio::post(service, [this, op]() {
- op_submit(op);
+ // If the parent op was already removed from its session by a map change, then
+ // ignore the completion here.
+ if (!op->session) {
+ return;
+ }
+
+ // While the following post is scheduled, this op is considered "in flight".
+ // If a new osdmap is published, this op might be cancelled/redriven, etc..
+ // before the following post is executed. Normally, an op would be protected
+ // from this in the messaging layer. However, we don't have that protection
+ // here. So we consider the following:
+ // 1. The op could be returned and freed (op->get() prevents this).
+ // 2. The op could be removed from its session but not yet resent.
+ // 3. The op could be redriven in the new epoch.
+ op->get();
+ auto epoch = op->target.epoch;
+
+ boost::asio::post(service, [this, op, ec, rc, epoch]() {
+
+ bool freed = op->get_nref() == 1;
+ op->put();
+ if (freed || !op->session || op->target.epoch != epoch) {
+ return;
+ }
+
+ shunique_lock rl(rwlock, ceph::acquire_shared);
+ ceph_assert(op->session);
+ unique_lock sl(op->session->lock);
+
+ if (rc != -EAGAIN) {
+ op->trace.event("post op complete");
+
+ // This function unlocks sl.
+ complete_op_reply(op, ec, op->session, sl, rc);
+ } else {
+ _session_op_remove(op->session, op);
+ sl.unlock();
+ op->split_op_tids.reset();
+ ceph_tid_t tid = 0;
+ _op_submit(op, rl, &tid);
+ }
});
}