Skip to content

Commit a67b941

Browse files
committed
Refactor Proj context handling behind a Context type
Introduce an internal Context enum (Owned/Shared) plus a reference-counted SharedContext and a per-thread SHARED_CONTEXT thread-local. Proj.ctx becomes a Context; helpers take it by value and Context's Drop destroys Owned contexts (replacing the explicit proj_context_destroy in Proj::drop). Move ctx() out of define_info_methods! into per-type accessors. Note: every constructor still produces Context::Owned. The Shared variant and thread_local_context() are wired up in the next commit: the work is split in order to allow more precise identification of leaks / memory corruption Signed-off-by: Stephan Hügel <shugel@tcd.ie>
1 parent cadb51d commit a67b941

3 files changed

Lines changed: 241 additions & 129 deletions

File tree

src/context.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ mod geo_types;
254254
#[macro_use]
255255
extern crate approx;
256256

257+
mod context;
257258
mod cstring_array;
258259
mod proj;
259260
mod transform;

0 commit comments

Comments
 (0)