before this change, a seastar::shared_mutex, a RWState and a
shared_promise are used for tracking the consumers of ObjectContext.
and all of the consumers are put into writers if the predicate function
evaluates to "false", and is awaken if the predicate function evaluates
to "true" afterwards in a polling loop waiting on the shared_promise,
which is in turn fulfilled once the last consumer of the given category
relinquishes the lock.
this approach has couple issues:
* it is heavy weighted. seastar::shared_mutex already tracks each of
the waiters' continuation using separate promise<>, and it does try
to reschedule them once a given consumer releases the last lock.
so it's like a design of a customized shared_mutex over a
shared_mutex.
* it is complicated. 3 variables for tracking the different
consumers of ObjectContext.
in this change,
* `tri_mutex` is introduced as a variant of the original
`seastar::shared_mutex` to track two different shared users in
addition to an exclusive user.
* replace `shared_mutex` with `tri_mutex` in `ObjectContext`, to
simplify the design.
* move recovery_read_marker into `ObjectContext`. assuming all
pending actions will be added as a waiter for the related
object context before they acquire the lock.