Skip to content

Commit

Permalink
Remove RT thread states
Browse files Browse the repository at this point in the history
Dynamic bindings handle everything we need for thread-specific var
values now.
  • Loading branch information
jeaye committed Jan 6, 2024
1 parent 63029d7 commit 2b38ff0
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 86 deletions.
2 changes: 2 additions & 0 deletions include/cpp/jank/native_persistent_string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ namespace jank
* 2. In-place char buffer (used for small categories)
* 3. As a large_storage instance, containing a pointer, size, and capacity
*/
/* NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) */
struct storage : allocator_type
{
/* TODO: What if we store a max of 22 chars and dedicate a byte for flags with no masking? */
Expand Down Expand Up @@ -490,6 +491,7 @@ namespace jank
constexpr void set_small_size(size_type const s) noexcept
{
assert(s <= max_small_size);
/* NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) */
store.small[s] = 0;
store.small[max_small_size] = value_type((max_small_size - s) << small_shift);
assert(get_category() == category::small && size() == s);
Expand Down
20 changes: 4 additions & 16 deletions include/cpp/jank/runtime/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ namespace jank::runtime

/* Adds the current ns to unqualified symbols and resolves the ns of qualified symbols.
* Does not intern. */
obj::symbol_ptr qualify_symbol(obj::symbol_ptr const &);
obj::symbol_ptr qualify_symbol(obj::symbol_ptr const &) const;
option<object_ptr> find_local(obj::symbol_ptr const &);

result<var_ptr, native_persistent_string> intern_var(obj::symbol_ptr const &);
Expand Down Expand Up @@ -92,25 +92,10 @@ namespace jank::runtime
folly::Synchronized<native_unordered_map<obj::symbol_ptr, ns_ptr>> namespaces;
folly::Synchronized<native_unordered_map<obj::symbol, obj::keyword_ptr>> keywords;

/* TODO: This can be forward declared and moved to the cpp. */
struct thread_state
{
thread_state(thread_state const&) = default;
thread_state(context &ctx);

var_ptr current_ns{};
var_ptr in_ns{};
context &rt_ctx;
};

thread_state& get_thread_state();
thread_state& get_thread_state(option<thread_state> init);

string_result<void> push_thread_bindings(obj::persistent_hash_map_ptr const bindings);
string_result<void> pop_thread_bindings();
option<thread_binding_frame> current_thread_binding_frame();

folly::Synchronized<native_unordered_map<std::thread::id, thread_state>> thread_states;
/* The analyze processor is reused across evaluations so we can keep the semantic information
* of previous code. This is essential for REPL use. */
/* TODO: This needs to be synchronized. */
Expand All @@ -124,6 +109,9 @@ namespace jank::runtime
native_persistent_string output_dir;
module::loader module_loader;

var_ptr current_ns_var{};
var_ptr in_ns_var{};

static thread_local native_unordered_map<context const*, std::list<thread_binding_frame>> thread_binding_frames;
};
}
4 changes: 2 additions & 2 deletions src/cpp/jank/analyze/local_frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ namespace jank::analyze
auto qualified_sym(make_box<runtime::obj::symbol>(*sym));
if(qualified_sym->ns.empty())
{
auto const state(rt_ctx.get_thread_state());
qualified_sym->ns = runtime::expect_object<runtime::ns>(state.current_ns->deref())->name->name;
qualified_sym->ns = runtime::expect_object<runtime::ns>
(rt_ctx.current_ns_var->deref())->name->name;
}
/* We use unique native names, just so var names don't clash with the underlying C++ API. */
lifted_var lv
Expand Down
85 changes: 21 additions & 64 deletions src/cpp/jank/runtime/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@

namespace jank::runtime
{
thread_local native_unordered_map
<context const*, std::list<thread_binding_frame>> context::thread_binding_frames{};
/* NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) */
thread_local decltype(context::thread_binding_frames) context::thread_binding_frames{};

context::context()
: context(util::cli::options{ })
Expand All @@ -32,17 +32,14 @@ namespace jank::runtime
, output_dir{ opts.compilation_path }
, module_loader{ *this, opts.class_path }
{
auto &t_state(get_thread_state());
auto const core(intern_ns(make_box<obj::symbol>("clojure.core")));
auto const ns_sym(make_box<obj::symbol>("clojure.core/*ns*"));
auto const current_ns_var(core->intern_var(ns_sym));
t_state.current_ns = current_ns_var;
t_state.current_ns->bind_root(core);
t_state.current_ns->dynamic.store(true);
current_ns_var = core->intern_var(ns_sym);
current_ns_var->bind_root(core);
current_ns_var->dynamic.store(true);

intern_ns(make_box<obj::symbol>("native"));

auto const in_ns_sym(make_box<obj::symbol>("clojure.core/in-ns"));
std::function<object_ptr (object_ptr)> in_ns_fn
(
[this](object_ptr const sym)
Expand All @@ -56,7 +53,7 @@ namespace jank::runtime
if constexpr(std::same_as<T, obj::symbol>)
{
auto const new_ns(intern_ns(typed_sym));
get_thread_state().current_ns->set(new_ns).expect_ok();
current_ns_var->set(new_ns).expect_ok();
return obj::nil::nil_const();
}
else
Expand All @@ -67,9 +64,9 @@ namespace jank::runtime
);
}
);
auto in_ns_var(intern_var(in_ns_sym).expect_ok());
auto const in_ns_sym(make_box<obj::symbol>("clojure.core/in-ns"));
in_ns_var = intern_var(in_ns_sym).expect_ok();
in_ns_var->bind_root(make_box<obj::native_function_wrapper>(in_ns_fn));
t_state.in_ns = in_ns_var;

/* TODO: Remove this once it can be defined in jank. */
auto const assert_sym(make_box<obj::symbol>("clojure.core/assert"));
Expand All @@ -96,9 +93,6 @@ namespace jank::runtime
obj::persistent_hash_map::create_unique
(std::make_pair(current_ns_var, current_ns_var->deref()))
).expect_ok();

/* Finally, enter the user ns. */
//in_ns_fn(make_box<obj::symbol>("user"));
}

context::context(context const &ctx)
Expand All @@ -113,7 +107,6 @@ namespace jank::runtime
for(auto const &ns : *ctx.namespaces.rlock())
{ ns_lock->insert({ ns.first, ns.second->clone(*this) }); }
*keywords.wlock() = *ctx.keywords.rlock();
*thread_states.wlock() = *ctx.thread_states.rlock();
}

auto &tbfs(thread_binding_frames[this]);
Expand All @@ -134,18 +127,24 @@ namespace jank::runtime
* pushed to the front of this one, we'd reverse the order. */
tbfs.push_back(std::move(frame));
}

auto const core(intern_ns(make_box<obj::symbol>("clojure.core")));
auto const ns_sym(make_box<obj::symbol>("clojure.core/*ns*"));
current_ns_var = core->intern_var(ns_sym);

auto const in_ns_sym(make_box<obj::symbol>("clojure.core/in-ns"));
in_ns_var = intern_var(in_ns_sym).expect_ok();
}

context::~context()
{ thread_binding_frames.erase(this); }

obj::symbol_ptr context::qualify_symbol(obj::symbol_ptr const &sym)
obj::symbol_ptr context::qualify_symbol(obj::symbol_ptr const &sym) const
{
obj::symbol_ptr qualified_sym{ sym };
if(qualified_sym->ns.empty())
{
auto const t_state(get_thread_state());
auto const current_ns(expect_object<ns>(t_state.current_ns->deref()));
auto const current_ns(expect_object<ns>(current_ns_var->deref()));
qualified_sym = make_box<obj::symbol>(current_ns->name->name, sym->name);
}
return qualified_sym;
Expand All @@ -170,8 +169,7 @@ namespace jank::runtime
}
else
{
auto const t_state(get_thread_state());
auto const current_ns(expect_object<ns>(t_state.current_ns->deref()));
auto const current_ns(expect_object<ns>(current_ns_var->deref()));
return current_ns->find_var(sym);
}
}
Expand Down Expand Up @@ -439,8 +437,7 @@ namespace jank::runtime
}
else
{
auto const t_state(get_thread_state());
auto const current_ns(expect_object<jank::runtime::ns>(t_state.current_ns->deref()));
auto const current_ns(expect_object<jank::runtime::ns>(current_ns_var->deref()));
sym.ns = current_ns->name->name;
}
}
Expand Down Expand Up @@ -565,45 +562,6 @@ namespace jank::runtime
return obj::nil::nil_const();
}

context::thread_state::thread_state(context &ctx)
: rt_ctx{ ctx }
{ }

context::thread_state& context::get_thread_state()
{ return get_thread_state(none); }
context::thread_state& context::get_thread_state(option<thread_state> init)
{
profile::timer timer{ "rt get_thread_state" };
auto const this_id(std::this_thread::get_id());
decltype(thread_states)::DataType::iterator found;

/* TODO: Can this just use an upgrade lock? */
/* Assume it's there and use a read lock. */
{
auto const locked_thread_states(thread_states.rlock());
/* Our read lock here is on the container; we're returning a mutable item, but
that's because the item itself is thread-local. */
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
found = const_cast
<native_unordered_map<std::thread::id, thread_state>&>(*locked_thread_states).find(this_id);
if(found != locked_thread_states->end())
{ return found->second; }
}

/* If it's not there, use a write lock and put it there (but check again first). */
{
auto const locked_thread_states(thread_states.wlock());
found = locked_thread_states->find(this_id);
if(found != locked_thread_states->end())
{ return found->second; }
else if(init.is_some())
{ found = locked_thread_states->emplace(this_id, std::move(init.unwrap())).first; }
else
{ found = locked_thread_states->emplace(this_id, thread_state{ *this }).first; }
return found->second;
}
}

string_result<void> context::push_thread_bindings(obj::persistent_hash_map_ptr const bindings)
{
thread_binding_frame frame{ obj::persistent_hash_map::empty() };
Expand Down Expand Up @@ -632,7 +590,7 @@ namespace jank::runtime
string_result<void> context::pop_thread_bindings()
{
auto &tbfs(thread_binding_frames[this]);
if(tbfs.empty()) [[unlikely]]
if(tbfs.empty())
{ return err("Mismatched thread binding pop"); }

tbfs.pop_front();
Expand All @@ -643,9 +601,8 @@ namespace jank::runtime
option<thread_binding_frame> context::current_thread_binding_frame()
{
auto &tbfs(thread_binding_frames[this]);
if(tbfs.empty()) [[likely]]
if(tbfs.empty())
{ return none; }
return tbfs.front();
}

}
8 changes: 4 additions & 4 deletions test/cpp/jank/runtime/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@ namespace jank::runtime
auto const locked_namespaces(ctx.namespaces.rlock());
CHECK(locked_namespaces->find(jank::make_box<obj::symbol>("clojure.core")) != locked_namespaces->end());
CHECK(locked_namespaces->find(jank::make_box<obj::symbol>("missing")) == locked_namespaces->end());
CHECK(expect_object<ns>(ctx.get_thread_state().current_ns->get_root())->name->equal(obj::symbol("", "clojure.core")));
CHECK(expect_object<ns>(ctx.current_ns_var->get_root())->name->equal(obj::symbol("", "clojure.core")));
}

TEST_CASE("Namespace changing")
{
context ctx;
{
auto const locked_namespaces(ctx.namespaces.rlock());
CHECK(expect_object<ns>(ctx.get_thread_state().current_ns->get_root())->name->equal(obj::symbol("", "clojure.core")));
CHECK(expect_object<ns>(ctx.current_ns_var->get_root())->name->equal(obj::symbol("", "clojure.core")));
CHECK(locked_namespaces->find(jank::make_box<obj::symbol>("test")) == locked_namespaces->end());
}
expect_object<obj::native_function_wrapper>(ctx.get_thread_state().in_ns->get_root())->call(jank::make_box<obj::symbol>("test"));
expect_object<obj::native_function_wrapper>(ctx.in_ns_var->get_root())->call(jank::make_box<obj::symbol>("test"));
{
auto const locked_namespaces(ctx.namespaces.rlock());
CHECK(locked_namespaces->find(jank::make_box<obj::symbol>("test")) != locked_namespaces->end());
CHECK(expect_object<ns>(ctx.get_thread_state().current_ns->get_root())->name->equal(obj::symbol("", "test")));
CHECK(expect_object<ns>(ctx.current_ns_var->get_root())->name->equal(obj::symbol("", "test")));
}
}
}

0 comments on commit 2b38ff0

Please sign in to comment.