Enum pgx::memcxt::PgMemoryContexts
source · [−]pub enum PgMemoryContexts {
Show 13 variants
CurrentMemoryContext,
TopMemoryContext,
PortalContext,
ErrorContext,
PostmasterContext,
CacheMemoryContext,
MessageContext,
TopTransactionContext,
CurTransactionContext,
For(MemoryContext),
Owned(OwnedMemoryContext),
Of(void_ptr),
Transient {
parent: MemoryContext,
name: &'static str,
min_context_size: u32,
initial_block_size: u32,
max_block_size: u32,
},
}
Expand description
An Enumeration of Postgres top-level MemoryContexts. Each have their own use and “lifetimes” as defined by Postgres’ memory management model.
It’s possible to deference any one of these (except Transient
) via the ::value()
method if
it’s necessary to pass the raw pointer to a Postgres function.
Additionally, the ::switch_to()
function, which takes a closure as its argument, executes the
closure within that MemoryContext
Variants
CurrentMemoryContext
Because it would be too much notational overhead to always pass an appropriate memory context to called routines, there always exists the notion of the current memory context CurrentMemoryContext. Without it, for example, the copyObject routines would need to be passed a context, as would function execution routines that return a pass-by-reference datatype. Similarly for routines that temporarily allocate space internally, but don’t return it to their caller? We certainly don’t want to clutter every call in the system with “here is a context to use for any temporary memory allocation you might want to do”.
The upshot of that reasoning, though, is that CurrentMemoryContext should generally point at a short-lifespan context if at all possible. During query execution it usually points to a context that gets reset after each tuple. Only in very circumscribed code should it ever point at a context having greater than transaction lifespan, since doing so risks permanent memory leaks.
TopMemoryContext
this is the actual top level of the context tree; every other context is a direct or indirect child of this one. Allocating here is essentially the same as “malloc”, because this context will never be reset or deleted. This is for stuff that should live forever, or for stuff that the controlling module will take care of deleting at the appropriate time. An example is fd.c’s tables of open files. Avoid allocating stuff here unless really necessary, and especially avoid running with CurrentMemoryContext pointing here.
PortalContext
this is not actually a separate context, but a global variable pointing to the per-portal context of the currently active execution portal. This can be used if it’s necessary to allocate storage that will live just as long as the execution of the current portal requires.
ErrorContext
this permanent context is switched into for error recovery processing, and then reset on completion of recovery. We arrange to have a few KB of memory available in it at all times. In this way, we can ensure that some memory is available for error recovery even if the backend has run out of memory otherwise. This allows out-of-memory to be treated as a normal ERROR condition, not a FATAL error.
PostmasterContext
this is the postmaster’s normal working context. After a backend is spawned, it can delete PostmasterContext to free its copy of memory the postmaster was using that it doesn’t need. Note that in non-EXEC_BACKEND builds, the postmaster’s copy of pg_hba.conf and pg_ident.conf data is used directly during authentication in backend processes; so backends can’t delete PostmasterContext until that’s done. (The postmaster has only TopMemoryContext, PostmasterContext, and ErrorContext — the remaining top-level contexts are set up in each backend during startup.)
CacheMemoryContext
permanent storage for relcache, catcache, and related modules. This will never be reset or deleted, either, so it’s not truly necessary to distinguish it from TopMemoryContext. But it seems worthwhile to maintain the distinction for debugging purposes. (Note: CacheMemoryContext has child contexts with shorter lifespans. For example, a child context is the best place to keep the subsidiary storage associated with a relcache entry; that way we can free rule parsetrees and so forth easily, without having to depend on constructing a reliable version of freeObject().)
MessageContext
this context holds the current command message from the frontend, as well as any derived storage that need only live as long as the current message (for example, in simple-Query mode the parse and plan trees can live here). This context will be reset, and any children deleted, at the top of each cycle of the outer loop of PostgresMain. This is kept separate from per-transaction and per-portal contexts because a query string might need to live either a longer or shorter time than any single transaction or portal.
TopTransactionContext
this holds everything that lives until end of the top-level transaction. This context will be reset, and all its children deleted, at conclusion of each top-level transaction cycle. In most cases you don’t want to allocate stuff directly here, but in CurTransactionContext; what does belong here is control information that exists explicitly to manage status across multiple subtransactions. Note: this context is NOT cleared immediately upon error; its contents will survive until the transaction block is exited by COMMIT/ROLLBACK.
CurTransactionContext
this holds data that has to survive until the end of the current transaction, and in particular will be needed at top-level transaction commit. When we are in a top-level transaction this is the same as TopTransactionContext, but in subtransactions it points to a child context. It is important to understand that if a subtransaction aborts, its CurTransactionContext is thrown away after finishing the abort processing; but a committed subtransaction’s CurTransactionContext is kept until top-level commit (unless of course one of the intermediate levels of subtransaction aborts). This ensures that we do not keep data from a failed subtransaction longer than necessary. Because of this behavior, you must be careful to clean up properly during subtransaction abort — the subtransaction’s state must be delinked from any pointers or lists kept in upper transactions, or you will have dangling pointers leading to a crash at top-level commit. An example of data kept here is pending NOTIFY messages, which are sent at top-level commit, but only if the generating subtransaction did not abort.
For(MemoryContext)
This represents a MemoryContext that was likely created via pg_sys::AllocSetContextCreateExtended.
That could be a MemoryContext you created yourself, or it could be one given to you from
Postgres. For example, the TupleTableSlot
struct has a field referencing the MemoryContext
in which slots are allocated.
Owned(OwnedMemoryContext)
A MemoryContext owned by Rust that will be freed when when Dropped
Of(void_ptr)
Use the MemoryContext in which the specified pointer was allocated.
It’s incredibly important that the specified pointer be one actually allocated by Postgres’ memory management system. Otherwise, it’s undefined behavior and will absolutely crash Postgres
Transient
Fields
parent: MemoryContext
name: &'static str
min_context_size: u32
initial_block_size: u32
max_block_size: u32
Create a temporary MemoryContext for use with ::switch_to()
. It gets deleted as soon
as ::switch_to()
exits.
Trying to use this context through [::value{}] will result in a panic!().
Implementations
sourceimpl PgMemoryContexts
impl PgMemoryContexts
sourcepub fn new(name: &str) -> PgMemoryContexts
pub fn new(name: &str) -> PgMemoryContexts
Create a new PgMemoryContext::Owned
sourcepub fn value(&self) -> MemoryContext
pub fn value(&self) -> MemoryContext
Retrieve the underlying Postgres *mut MemoryContextData
This works for every type except the ::Transient
type.
sourcepub fn set_as_current(&self) -> PgMemoryContexts
pub fn set_as_current(&self) -> PgMemoryContexts
Set this MemoryContext as the CurrentMemoryContext, returning whatever
CurrentMemoryContext` is
sourcepub fn reset(&mut self)
pub fn reset(&mut self)
Release all space allocated within a context and delete all its descendant contexts (but not the context itself).
sourcepub fn switch_to<R, F: FnOnce(&mut PgMemoryContexts) -> R + UnwindSafe + RefUnwindSafe>(
&mut self,
f: F
) -> R
pub fn switch_to<R, F: FnOnce(&mut PgMemoryContexts) -> R + UnwindSafe + RefUnwindSafe>(
&mut self,
f: F
) -> R
Run the specified function “within” the MemoryContext
represented by this enum.
The important implementation detail is that Postgres’ CurrentMemoryContext
is changed
to be this context, the function is run so that all Postgres memory allocations happen
within that context, and then CurrentMemoryContext
is restored to what it was before
we started.
Examples
use pgx::*;
#[pg_guard]
pub fn do_something() -> pg_sys::ItemPointer {
PgMemoryContexts::TopTransactionContext.switch_to(|context| {
// allocate a new ItemPointerData, but inside the TopTransactionContext
let tid = PgBox::<pg_sys::ItemPointerData>::alloc();
// do something with the tid and then return it.
// Note that it stays allocated here in the TopTransactionContext
tid.into_pg()
})
}
sourcepub fn pstrdup(&self, s: &str) -> *mut c_char
pub fn pstrdup(&self, s: &str) -> *mut c_char
Duplicate a Rust &str
into a Postgres-allocated “char *”
Examples
use pgx::PgMemoryContexts;
let copy = PgMemoryContexts::CurrentMemoryContext.pstrdup("make a copy of this");
sourcepub unsafe fn copy_ptr_into<T>(&mut self, src: *mut T, len: usize) -> *mut T
pub unsafe fn copy_ptr_into<T>(&mut self, src: *mut T, len: usize) -> *mut T
Copies len
bytes, starting at src
into this memory context and
returns a raw *mut T
pointer to the newly allocated location
sourcepub fn palloc(&mut self, len: usize) -> *mut c_void
pub fn palloc(&mut self, len: usize) -> *mut c_void
Allocate memory in this context, which will be free’d whenever Postgres deletes this MemoryContext
pub fn palloc_struct<T>(&mut self) -> *mut T
pub fn palloc0_struct<T>(&mut self) -> *mut T
sourcepub fn palloc_slice<'a, T>(&mut self, len: usize) -> &'a mut [T]ⓘNotable traits for &'_ [u8]impl<'_> Read for &'_ [u8]impl<'_> Write for &'_ mut [u8]
pub fn palloc_slice<'a, T>(&mut self, len: usize) -> &'a mut [T]ⓘNotable traits for &'_ [u8]impl<'_> Read for &'_ [u8]impl<'_> Write for &'_ mut [u8]
Allocate a slice in this context, which will be free’d whenever Postgres deletes this MemoryContext
sourcepub fn palloc0_slice<'a, T>(&mut self, len: usize) -> &'a mut [T]ⓘNotable traits for &'_ [u8]impl<'_> Read for &'_ [u8]impl<'_> Write for &'_ mut [u8]
pub fn palloc0_slice<'a, T>(&mut self, len: usize) -> &'a mut [T]ⓘNotable traits for &'_ [u8]impl<'_> Read for &'_ [u8]impl<'_> Write for &'_ mut [u8]
Allocate a slice in this context, where the memory is zero’d, which will be free’d whenever Postgres deletes this MemoryContext
sourcepub fn palloc0(&mut self, len: usize) -> *mut c_void
pub fn palloc0(&mut self, len: usize) -> *mut c_void
Allocate memory in this context, which will be free’d whenever Postgres deletes this MemoryContext
The allocated memory is zero’d
pub fn leak_and_drop_on_delete<T>(&mut self, v: T) -> *mut T
Trait Implementations
Auto Trait Implementations
impl RefUnwindSafe for PgMemoryContexts
impl !Send for PgMemoryContexts
impl !Sync for PgMemoryContexts
impl Unpin for PgMemoryContexts
impl UnwindSafe for PgMemoryContexts
Blanket Implementations
sourceimpl<T> BorrowMut<T> for T where
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
const: unstable · sourcepub fn borrow_mut(&mut self) -> &mut T
pub fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
sourceimpl<T> Instrument for T
impl<T> Instrument for T
sourcefn instrument(self, span: Span) -> Instrumented<Self>ⓘNotable traits for Instrumented<T>impl<T> Future for Instrumented<T> where
T: Future, type Output = <T as Future>::Output;
fn instrument(self, span: Span) -> Instrumented<Self>ⓘNotable traits for Instrumented<T>impl<T> Future for Instrumented<T> where
T: Future, type Output = <T as Future>::Output;
T: Future, type Output = <T as Future>::Output;
sourcefn in_current_span(self) -> Instrumented<Self>ⓘNotable traits for Instrumented<T>impl<T> Future for Instrumented<T> where
T: Future, type Output = <T as Future>::Output;
fn in_current_span(self) -> Instrumented<Self>ⓘNotable traits for Instrumented<T>impl<T> Future for Instrumented<T> where
T: Future, type Output = <T as Future>::Output;
T: Future, type Output = <T as Future>::Output;
impl<D> OwoColorize for D
impl<D> OwoColorize for D
fn fg<C>(&self) -> FgColorDisplay<'_, C, Self> where
C: Color,
fn fg<C>(&self) -> FgColorDisplay<'_, C, Self> where
C: Color,
Set the foreground color generically Read more
fn bg<C>(&self) -> BgColorDisplay<'_, C, Self> where
C: Color,
fn bg<C>(&self) -> BgColorDisplay<'_, C, Self> where
C: Color,
Set the background color generically. Read more
fn black(&'a self) -> FgColorDisplay<'a, Black, Self>
fn black(&'a self) -> FgColorDisplay<'a, Black, Self>
Change the foreground color to black
fn on_black(&'a self) -> BgColorDisplay<'a, Black, Self>
fn on_black(&'a self) -> BgColorDisplay<'a, Black, Self>
Change the background color to black
fn red(&'a self) -> FgColorDisplay<'a, Red, Self>
fn red(&'a self) -> FgColorDisplay<'a, Red, Self>
Change the foreground color to red
fn on_red(&'a self) -> BgColorDisplay<'a, Red, Self>
fn on_red(&'a self) -> BgColorDisplay<'a, Red, Self>
Change the background color to red
fn green(&'a self) -> FgColorDisplay<'a, Green, Self>
fn green(&'a self) -> FgColorDisplay<'a, Green, Self>
Change the foreground color to green
fn on_green(&'a self) -> BgColorDisplay<'a, Green, Self>
fn on_green(&'a self) -> BgColorDisplay<'a, Green, Self>
Change the background color to green
fn yellow(&'a self) -> FgColorDisplay<'a, Yellow, Self>
fn yellow(&'a self) -> FgColorDisplay<'a, Yellow, Self>
Change the foreground color to yellow
fn on_yellow(&'a self) -> BgColorDisplay<'a, Yellow, Self>
fn on_yellow(&'a self) -> BgColorDisplay<'a, Yellow, Self>
Change the background color to yellow
fn blue(&'a self) -> FgColorDisplay<'a, Blue, Self>
fn blue(&'a self) -> FgColorDisplay<'a, Blue, Self>
Change the foreground color to blue
fn on_blue(&'a self) -> BgColorDisplay<'a, Blue, Self>
fn on_blue(&'a self) -> BgColorDisplay<'a, Blue, Self>
Change the background color to blue
fn magenta(&'a self) -> FgColorDisplay<'a, Magenta, Self>
fn magenta(&'a self) -> FgColorDisplay<'a, Magenta, Self>
Change the foreground color to magenta
fn on_magenta(&'a self) -> BgColorDisplay<'a, Magenta, Self>
fn on_magenta(&'a self) -> BgColorDisplay<'a, Magenta, Self>
Change the background color to magenta
fn purple(&'a self) -> FgColorDisplay<'a, Magenta, Self>
fn purple(&'a self) -> FgColorDisplay<'a, Magenta, Self>
Change the foreground color to purple
fn on_purple(&'a self) -> BgColorDisplay<'a, Magenta, Self>
fn on_purple(&'a self) -> BgColorDisplay<'a, Magenta, Self>
Change the background color to purple
fn cyan(&'a self) -> FgColorDisplay<'a, Cyan, Self>
fn cyan(&'a self) -> FgColorDisplay<'a, Cyan, Self>
Change the foreground color to cyan
fn on_cyan(&'a self) -> BgColorDisplay<'a, Cyan, Self>
fn on_cyan(&'a self) -> BgColorDisplay<'a, Cyan, Self>
Change the background color to cyan
fn white(&'a self) -> FgColorDisplay<'a, White, Self>
fn white(&'a self) -> FgColorDisplay<'a, White, Self>
Change the foreground color to white
fn on_white(&'a self) -> BgColorDisplay<'a, White, Self>
fn on_white(&'a self) -> BgColorDisplay<'a, White, Self>
Change the background color to white
fn default_color(&'a self) -> FgColorDisplay<'a, Default, Self>
fn default_color(&'a self) -> FgColorDisplay<'a, Default, Self>
Change the foreground color to the terminal default
fn on_default_color(&'a self) -> BgColorDisplay<'a, Default, Self>
fn on_default_color(&'a self) -> BgColorDisplay<'a, Default, Self>
Change the background color to the terminal default
fn bright_black(&'a self) -> FgColorDisplay<'a, BrightBlack, Self>
fn bright_black(&'a self) -> FgColorDisplay<'a, BrightBlack, Self>
Change the foreground color to bright black
fn on_bright_black(&'a self) -> BgColorDisplay<'a, BrightBlack, Self>
fn on_bright_black(&'a self) -> BgColorDisplay<'a, BrightBlack, Self>
Change the background color to bright black
fn bright_red(&'a self) -> FgColorDisplay<'a, BrightRed, Self>
fn bright_red(&'a self) -> FgColorDisplay<'a, BrightRed, Self>
Change the foreground color to bright red
fn on_bright_red(&'a self) -> BgColorDisplay<'a, BrightRed, Self>
fn on_bright_red(&'a self) -> BgColorDisplay<'a, BrightRed, Self>
Change the background color to bright red
fn bright_green(&'a self) -> FgColorDisplay<'a, BrightGreen, Self>
fn bright_green(&'a self) -> FgColorDisplay<'a, BrightGreen, Self>
Change the foreground color to bright green
fn on_bright_green(&'a self) -> BgColorDisplay<'a, BrightGreen, Self>
fn on_bright_green(&'a self) -> BgColorDisplay<'a, BrightGreen, Self>
Change the background color to bright green
fn bright_yellow(&'a self) -> FgColorDisplay<'a, BrightYellow, Self>
fn bright_yellow(&'a self) -> FgColorDisplay<'a, BrightYellow, Self>
Change the foreground color to bright yellow
fn on_bright_yellow(&'a self) -> BgColorDisplay<'a, BrightYellow, Self>
fn on_bright_yellow(&'a self) -> BgColorDisplay<'a, BrightYellow, Self>
Change the background color to bright yellow
fn bright_blue(&'a self) -> FgColorDisplay<'a, BrightBlue, Self>
fn bright_blue(&'a self) -> FgColorDisplay<'a, BrightBlue, Self>
Change the foreground color to bright blue
fn on_bright_blue(&'a self) -> BgColorDisplay<'a, BrightBlue, Self>
fn on_bright_blue(&'a self) -> BgColorDisplay<'a, BrightBlue, Self>
Change the background color to bright blue
fn bright_magenta(&'a self) -> FgColorDisplay<'a, BrightMagenta, Self>
fn bright_magenta(&'a self) -> FgColorDisplay<'a, BrightMagenta, Self>
Change the foreground color to bright magenta
fn on_bright_magenta(&'a self) -> BgColorDisplay<'a, BrightMagenta, Self>
fn on_bright_magenta(&'a self) -> BgColorDisplay<'a, BrightMagenta, Self>
Change the background color to bright magenta
fn bright_purple(&'a self) -> FgColorDisplay<'a, BrightMagenta, Self>
fn bright_purple(&'a self) -> FgColorDisplay<'a, BrightMagenta, Self>
Change the foreground color to bright purple
fn on_bright_purple(&'a self) -> BgColorDisplay<'a, BrightMagenta, Self>
fn on_bright_purple(&'a self) -> BgColorDisplay<'a, BrightMagenta, Self>
Change the background color to bright purple
fn bright_cyan(&'a self) -> FgColorDisplay<'a, BrightCyan, Self>
fn bright_cyan(&'a self) -> FgColorDisplay<'a, BrightCyan, Self>
Change the foreground color to bright cyan
fn on_bright_cyan(&'a self) -> BgColorDisplay<'a, BrightCyan, Self>
fn on_bright_cyan(&'a self) -> BgColorDisplay<'a, BrightCyan, Self>
Change the background color to bright cyan
fn bright_white(&'a self) -> FgColorDisplay<'a, BrightWhite, Self>
fn bright_white(&'a self) -> FgColorDisplay<'a, BrightWhite, Self>
Change the foreground color to bright white
fn on_bright_white(&'a self) -> BgColorDisplay<'a, BrightWhite, Self>
fn on_bright_white(&'a self) -> BgColorDisplay<'a, BrightWhite, Self>
Change the background color to bright white
fn bold(&'a self) -> BoldDisplay<'a, Self>
fn bold(&'a self) -> BoldDisplay<'a, Self>
Make the text bold
fn dimmed(&'a self) -> DimDisplay<'a, Self>
fn dimmed(&'a self) -> DimDisplay<'a, Self>
Make the text dim
fn italic(&'a self) -> ItalicDisplay<'a, Self>
fn italic(&'a self) -> ItalicDisplay<'a, Self>
Make the text italicized
fn underline(&'a self) -> UnderlineDisplay<'a, Self>
fn underline(&'a self) -> UnderlineDisplay<'a, Self>
Make the text italicized
fn blink(&'a self) -> BlinkDisplay<'a, Self>
fn blink(&'a self) -> BlinkDisplay<'a, Self>
Make the text blink
fn blink_fast(&'a self) -> BlinkFastDisplay<'a, Self>
fn blink_fast(&'a self) -> BlinkFastDisplay<'a, Self>
Make the text blink (but fast!)
fn reversed(&'a self) -> ReversedDisplay<'a, Self>
fn reversed(&'a self) -> ReversedDisplay<'a, Self>
Swap the foreground and background colors
Hide the text
fn strikethrough(&'a self) -> StrikeThroughDisplay<'a, Self>
fn strikethrough(&'a self) -> StrikeThroughDisplay<'a, Self>
Cross out the text
fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self> where
Color: DynColor,
fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self> where
Color: DynColor,
Set the foreground color at runtime. Only use if you do not know which color will be used at
compile-time. If the color is constant, use either OwoColorize::fg
or
a color-specific method, such as OwoColorize::green
, Read more
fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self> where
Color: DynColor,
fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self> where
Color: DynColor,
Set the background color at runtime. Only use if you do not know what color to use at
compile-time. If the color is constant, use either OwoColorize::bg
or
a color-specific method, such as OwoColorize::on_yellow
, Read more
fn fg_rgb<const R: u8, const G: u8, const B: u8>(
&self
) -> FgColorDisplay<'_, CustomColor<R, G, B>, Self>
fn fg_rgb<const R: u8, const G: u8, const B: u8>(
&self
) -> FgColorDisplay<'_, CustomColor<R, G, B>, Self>
Set the foreground color to a specific RGB value.
fn bg_rgb<const R: u8, const G: u8, const B: u8>(
&self
) -> BgColorDisplay<'_, CustomColor<R, G, B>, Self>
fn bg_rgb<const R: u8, const G: u8, const B: u8>(
&self
) -> BgColorDisplay<'_, CustomColor<R, G, B>, Self>
Set the background color to a specific RGB value.
fn truecolor(&self, r: u8, g: u8, b: u8) -> FgDynColorDisplay<'_, Rgb, Self>
fn truecolor(&self, r: u8, g: u8, b: u8) -> FgDynColorDisplay<'_, Rgb, Self>
Sets the foreground color to an RGB value.
fn on_truecolor(&self, r: u8, g: u8, b: u8) -> BgDynColorDisplay<'_, Rgb, Self>
fn on_truecolor(&self, r: u8, g: u8, b: u8) -> BgDynColorDisplay<'_, Rgb, Self>
Sets the background color to an RGB value.
impl<V, T> VZip<V> for T where
V: MultiLane<T>,
impl<V, T> VZip<V> for T where
V: MultiLane<T>,
pub fn vzip(self) -> V
sourceimpl<T> WithSubscriber for T
impl<T> WithSubscriber for T
sourcefn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>ⓘNotable traits for WithDispatch<T>impl<T> Future for WithDispatch<T> where
T: Future, type Output = <T as Future>::Output;
where
S: Into<Dispatch>,
fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>ⓘNotable traits for WithDispatch<T>impl<T> Future for WithDispatch<T> where
T: Future, type Output = <T as Future>::Output;
where
S: Into<Dispatch>,
T: Future, type Output = <T as Future>::Output;
Attaches the provided Subscriber
to this type, returning a
WithDispatch
wrapper. Read more
sourcefn with_current_subscriber(self) -> WithDispatch<Self>ⓘNotable traits for WithDispatch<T>impl<T> Future for WithDispatch<T> where
T: Future, type Output = <T as Future>::Output;
fn with_current_subscriber(self) -> WithDispatch<Self>ⓘNotable traits for WithDispatch<T>impl<T> Future for WithDispatch<T> where
T: Future, type Output = <T as Future>::Output;
T: Future, type Output = <T as Future>::Output;
Attaches the current default Subscriber
to this type, returning a
WithDispatch
wrapper. Read more