Skip to content

Commit

Permalink
Update to match latest reference implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Paul E. McKenney <[email protected]>
  • Loading branch information
paulmck committed Jul 31, 2017
1 parent 5d8aa6e commit c82ce9e
Showing 1 changed file with 64 additions and 26 deletions.
90 changes: 64 additions & 26 deletions RCU-bindings.tex
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ \section{Introduction}
WG21/P0098R1 (``Towards Implementation and Use of \co{memory_order_consume}'').

Specifically, this document proposes
\co{rcu_reader} (Figure~\ref{fig:RAII RCU Readers}) and
\co{rcu_obj_base} (Figure~\ref{fig:RCU Callbacks: Intrusive Approach}).
\co{rcu_reader} (Figure~\ref{fig:RAII RCU Readers}),
\co{rcu_obj_base} (Figure~\ref{fig:Retiring RCU-Protected Objects}), and
\co{rcu_updater} (Figure~\ref{sec:RCU Updaters}).

Section~\ref{sec:Existing C-Language RCU API} presents the base (C-style) RCU API,
Section~\ref{sec:RAII RCU Readers} presents a proposal for scoped RCU readers,
Expand Down Expand Up @@ -248,11 +249,14 @@ \subsection{Existing C-Language RCU API And C++}

Both \co{rcu_read_lock()} and \co{rcu_read_unlock()} are encapsulated
in a C++ RAII class named \co{rcu_reader}.
The \co{synchronize_rcu()} function may be emulated using \co{call_rcu()},
and is therefore omitted from this initial proposal. If needed, it
will be added later.
The \co{rcu_barrier()} function is exposed via the
\co{std::rcu_reader::barrier()} static member function.
The \co{synchronize_rcu()} function is exposed via the
\co{std::rcu_updater::synchronize()} static member function, and
the \co{rcu_barrier()} function is exposed via the
\co{std::rcu_updater::barrier()} static member function.
The \co{call_rcu()} function is exposed via the
\co{std::rcu_obj_base<T,D>retire()} member function,
and a non-intrusive variant of \co{call_rcu()} is exposed via the
\co{std::rcu_retire()} templated free function.

We expect that \co{rcu_register_thread()} and \co{rcu_unregister_thread()}
will be buried into the thread-creation and thread-exit portions of the
Expand All @@ -274,14 +278,16 @@ \section{RAII RCU Readers}
1 class rcu_reader {
2 public:
3 rcu_reader() noexcept;
4 rcu_reader(std::nullptr_t) noexcept;
4 rcu_reader(std::defer_lock_t) noexcept;
5 rcu_reader(const rcu_reader &) = delete;
6 rcu_reader(rcu_reader &&other) noexcept;
7 rcu_reader& operator=(const rcu_reader&) = delete;
8 rcu_reader& operator=(rcu_reader&& other) noexcept;
9 ~rcu_reader() noexcept;
10 static void barrier() noexcept
11 };
10 void swap(rcu_reader& other) noexcept;
11 void lock() noexcept;
12 void unlock() noexcept;
13 };
\end{verbatim}
}
\caption{RAII RCU Readers}
Expand All @@ -303,11 +309,6 @@ \section{RAII RCU Readers}
extended across function boundaries via \co{std::forward} and
\co{std::move}.

The static member function \co{barrier()} maps to the C-language
free function \co{rcu_barrier()}, which waits for all previously
retired object to be reclaimed.
{\bf Question:}~ Should this instead be a free function?

\section{Retiring RCU-Protected Objects}
\label{sec:Retiring RCU-Protected Objects}

Expand All @@ -324,21 +325,24 @@ \section{Retiring RCU-Protected Objects}
\begin{figure}[tbp]
{ \scriptsize
\begin{verbatim}
1 template<typename T, typename D = default_delete<T>, bool E = is_empty<D>::value>
2 class rcu_obj_base: private rcu_head {
1 template<typename T, typename D = default_delete<T>>
2 class rcu_obj_base {
3 public:
4 void retire(D d = {});
5 };
6
7 template<typename T, typename D = default_delete<T>>
8 void rcu_retire(T *p, D d = {});
\end{verbatim}
}
\caption{RCU Callbacks: Intrusive Approach (Preferred)}
\label{fig:RCU Callbacks: Intrusive Approach}
\caption{Retiring RCU-Protected Objects}
\label{fig:Retiring RCU-Protected Objects}
\end{figure}

The \co{rcu_obj_base} class provides a \co{retire()} method that
takes a deleter,,
takes a deleter,
as shown in
Figure~\ref{fig:RCU Callbacks: Intrusive Approach}.
Figure~\ref{fig:Retiring RCU-Protected Objects}.
The deleter's operator() is invoked after a grace period.
The deleter type defaults to \co{std::default_delete<T>},
but one could also use a
Expand All @@ -347,14 +351,42 @@ \section{Retiring RCU-Protected Objects}
\co{void(*)(T*)}, or a lambda type.
We recommend avoiding deleter types such as \co{std::function<void(T*)>}
(and also any other type requiring memory allocation) because
allocating memory on the free path can result in out-of-memory deadlocks.
allocating memory on the free path can result in out-of-memory deadlocks,
but we nevertheless recognize that C++ applications that assume ample
memory might use such deleters for convenience.

The \co{call_rcu()} C-language free function is used to implement
\co{retire()}.

Please note that this is the preferred approach, and is being proposed
for standardization.

\section{RCU Updaters}
\label{sec:RCU Updaters}

\begin{figure}[tbp]
{ \scriptsize
\begin{verbatim}
1 class rcu_updater {
2 static void synchronize() noexcept;
3 static void barrier() noexcept;
4 };
\end{verbatim}
}
\caption{RCU Updaters}
\label{fig:RCU Updaters}
\end{figure}

RCU updaters can use the \co{rcu_updater} class shown in
Figure~\ref{fig:RCU Updaters}.
This class contains a \co{synchronize()} static member function,
which maps to the C-language \co{synchronize_rcu()} function, which
waits for all pre-existing RCU readers to complete.
It also contains the \co{barrier()} static member function,
which maps to the C-language \co{rcu_barrier()} member function,
which waits for all pending \co{retire()} and \co{rcu_retire()}
deleters to be invoked.

\section{Hazard Pointers and RCU: Which to Use?}
\label{sec:Hazard Pointers and RCU: Which to Use?}

Expand Down Expand Up @@ -465,9 +497,11 @@ \section{Hazard Pointers and RCU: Which to Use?}

\begin{enumerate}
\item The \co{hazptr_obj_base} class is analogous to \co{rcu_obj_base}.
\item The \co{hazptr_domain} class is analogous to \co{rcu_domain}.
\item There is no RCU counterpart to \co{hazptr_domain}, in part because
RCU does not explicitly track read-side references to specific
objects.
\item The private \co{hazptr_obj} class is analogous to the pre-existing
\co{rcu_head} struct.
\co{rcu_head} struct used in many RCU implementations.
Because this is a private hazard-pointers class, there is no
need to have compatible names.
\item There is no RCU class analogous to \co{hazptr_rec} because
Expand All @@ -477,6 +511,9 @@ \section{Hazard Pointers and RCU: Which to Use?}
class.
This is because hazard pointers does not have (or need)
a counterpart to \co{rcu_read_lock()} and \co{rcu_read_unlock()}.
\item There is no hazard pointers counterpart to the \co{rcu_retire()}
templated free function because no hazard-pointers user has
expressed a need for it.
\end{enumerate}

\section{Summary}
Expand All @@ -486,8 +523,9 @@ \section{Summary}
RCU implementation, which has been tested against the userspace RCU
library.
Specifically, this document proposes
\co{rcu_reader} (Figure~\ref{fig:RAII RCU Readers}) and
\co{rcu_obj_base} (Figure~\ref{fig:RCU Callbacks: Intrusive Approach}).
\co{rcu_reader} (Figure~\ref{fig:RAII RCU Readers}),
\co{rcu_obj_base} (Figure~\ref{fig:Retiring RCU-Protected Objects}), and
\co{rcu_updater} (Figure~\ref{sec:RCU Updaters}).
We believe that these bindings are also appropriate for the type-oblivious
C++ RCU implementations that information-hiding considerations are likely
to favor.
Expand Down

0 comments on commit c82ce9e

Please sign in to comment.