|
| 1 | +use std::rc::Rc; |
| 2 | + |
| 3 | +use proj_sys::{PJ_CONTEXT, proj_context_clone, proj_context_create, proj_context_destroy}; |
| 4 | + |
| 5 | +/// A PROJ context (`PJ_CONTEXT`). |
| 6 | +/// |
| 7 | +/// A thin wrapper around the raw pointer: dropping a `Context` calls `proj_context_destroy`. |
| 8 | +/// This is the only type responsible for destroying a context, whether the context belongs to a |
| 9 | +/// single [`Proj`](crate::Proj) or is shared between many via [`ProjContext::Shared`]. |
| 10 | +pub(crate) struct Context { |
| 11 | + ctx: *mut PJ_CONTEXT, |
| 12 | +} |
| 13 | + |
| 14 | +impl Context { |
| 15 | + /// Create a new PROJ context. |
| 16 | + pub(crate) fn new() -> Self { |
| 17 | + Self { |
| 18 | + ctx: unsafe { proj_context_create() }, |
| 19 | + } |
| 20 | + } |
| 21 | + |
| 22 | + /// The raw context pointer, for passing to PROJ FFI calls. |
| 23 | + pub(crate) fn as_ptr(&self) -> *mut PJ_CONTEXT { |
| 24 | + self.ctx |
| 25 | + } |
| 26 | +} |
| 27 | + |
| 28 | +impl Clone for Context { |
| 29 | + /// Clone the underlying context via `proj_context_clone`, yielding an independently owned |
| 30 | + /// `Context`. |
| 31 | + fn clone(&self) -> Self { |
| 32 | + Self { |
| 33 | + ctx: unsafe { proj_context_clone(self.ctx) }, |
| 34 | + } |
| 35 | + } |
| 36 | +} |
| 37 | + |
| 38 | +impl Drop for Context { |
| 39 | + fn drop(&mut self) { |
| 40 | + // The single place a PROJ context is destroyed. This can run during thread-local teardown |
| 41 | + // (for the shared per-thread context), so it must not re-enter the `SHARED_CONTEXT` |
| 42 | + // thread-local. We deliberately do not call proj_cleanup() (see the note in `Drop for |
| 43 | + // Proj`). |
| 44 | + unsafe { proj_context_destroy(self.ctx) }; |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +/// The context backing a [`Proj`](crate::Proj): either uniquely owned by that `Proj`, or the |
| 49 | +/// per-thread context shared between every `Proj` created via [`Proj::new`](crate::Proj::new) and |
| 50 | +/// [`Proj::new_known_crs`](crate::Proj::new_known_crs). |
| 51 | +/// |
| 52 | +/// `Shared` reference counts the context so that it outlives every `Proj` that uses it, regardless |
| 53 | +/// of the order in which the thread-local and any surviving `Proj` instances are dropped at thread |
| 54 | +/// exit. |
| 55 | +pub(crate) enum ProjContext { |
| 56 | + Owned(Context), |
| 57 | + // Wired into Proj::new/new_known_crs in a follow-up commit. |
| 58 | + #[allow(dead_code)] |
| 59 | + Shared(Rc<Context>), |
| 60 | +} |
| 61 | + |
| 62 | +impl ProjContext { |
| 63 | + fn context(&self) -> &Context { |
| 64 | + match self { |
| 65 | + ProjContext::Owned(ctx) => ctx, |
| 66 | + ProjContext::Shared(ctx) => ctx.as_ref(), |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + /// The raw context pointer, for passing to PROJ FFI calls. |
| 71 | + pub(crate) fn as_ptr(&self) -> *mut PJ_CONTEXT { |
| 72 | + self.context().as_ptr() |
| 73 | + } |
| 74 | + |
| 75 | + /// Clone the underlying context into a new, independently owned `ProjContext::Owned`. |
| 76 | + /// |
| 77 | + /// Used where a derived `Proj` needs its own context rather than continuing to share the |
| 78 | + /// per-thread one. |
| 79 | + pub(crate) fn clone_owned(&self) -> Self { |
| 80 | + ProjContext::Owned(self.context().clone()) |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +thread_local! { |
| 85 | + /// One PROJ context per thread, reused by `Proj::new`/`Proj::new_known_crs`. Creating a fresh |
| 86 | + /// context per object opens a new connection to the PROJ database with cold caches, which |
| 87 | + /// dominates `Proj` construction time (see https://github.com/georust/proj/issues/256). |
| 88 | + static SHARED_CONTEXT: Rc<Context> = Rc::new(Context::new()); |
| 89 | +} |
| 90 | + |
| 91 | +/// Return a reference-counted handle to the calling thread's shared PROJ context. |
| 92 | +#[allow(dead_code)] // wired into Proj::new/new_known_crs in a follow-up commit |
| 93 | +pub(crate) fn thread_local_context() -> ProjContext { |
| 94 | + ProjContext::Shared(SHARED_CONTEXT.with(Rc::clone)) |
| 95 | +} |
0 commit comments