Skip to content

Commit fbbc1c1

Browse files
authored
Use a single lock in Namespace instances rather than Atoms (#494)
* Use a single lock in Namespace instances rather than Atoms * Change the Namespace.add_alias signature * Fix the core, obviously
1 parent e2f6715 commit fbbc1c1

File tree

9 files changed

+96
-89
lines changed

9 files changed

+96
-89
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
* Added `defn-`, `declare`, and `defonce` macros (#480)
1414
* Added EDN reader in the `basilisp.edn` namespace (#477)
1515
* Added line, column, and file information to reader `SyntaxError`s (#488)
16+
* Added context information to the `CompilerException` string output (#493)
1617

1718
### Changed
1819
* Change the default user namespace to `basilisp.user` (#466)
1920
* Changed multi-methods to use a `threading.Lock` internally rather than an Atom (#478)
2021
* Changed the Basilisp module type from `types.ModuleType` to a custom subtype with support for custom attributes (#482)
2122
* Basilisp's runtime function `Namespace.get_or_create` no longer refers `basilisp.core` by default, which allows callers to exclude `basilisp.core` names in the `ns` macro (#481)
23+
* Namespaces now use a single internal lock rather than putting each property inside of an Atom (#494)
2224

2325
### Fixed
2426
* Fixed a reader bug where no exception was being thrown splicing reader conditional forms appeared outside of valid splicing contexts (#470)

src/basilisp/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def bootstrap_repl(which_ns: str) -> types.ModuleType:
6161
assert core_ns is not None
6262
ns.refer_all(core_ns)
6363
repl_module = importlib.import_module(REPL_NS)
64-
ns.add_alias(sym.symbol(REPL_NS), repl_ns)
64+
ns.add_alias(repl_ns, sym.symbol(REPL_NS))
6565
ns.refer_all(repl_ns)
6666
return repl_module
6767

src/basilisp/core.lpy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3471,7 +3471,7 @@
34713471
(importlib/import-module (name ns-sym))
34723472
(let [new-ns (the-ns ns-sym)]
34733473
;; Add the namespace alias
3474-
(.add-alias current-ns (or (:as opts) ns-sym) new-ns)
3474+
(.add-alias current-ns new-ns (or (:as opts) ns-sym))
34753475

34763476
;; Add refers
34773477
(cond

src/basilisp/edn.lpy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@
212212
(update m k inc)
213213
(assoc m k 1)))
214214
{})
215-
(filter (fn [[k n]] (> n 1)))
215+
(filter (fn [[_ n]] (> n 1)))
216216
(ffirst))}))
217217
coll-set)))]
218218
(read-coll reader opts set-if-valid "}" "set")))

src/basilisp/lang/runtime.py

Lines changed: 70 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -425,20 +425,18 @@ def __init__(self, name: sym.Symbol, module: BasilispModule = None) -> None:
425425
self._meta: Optional[IPersistentMap] = None
426426
self._lock = threading.Lock()
427427

428-
self._aliases: Atom[NamespaceMap] = Atom(lmap.Map.empty())
429-
self._imports: Atom[ModuleMap] = Atom(
430-
lmap.map(
431-
dict(
432-
map(
433-
lambda s: (s, importlib.import_module(s.name)),
434-
Namespace.DEFAULT_IMPORTS.deref(),
435-
)
428+
self._aliases: NamespaceMap = lmap.Map.empty()
429+
self._imports: ModuleMap = lmap.map(
430+
dict(
431+
map(
432+
lambda s: (s, importlib.import_module(s.name)),
433+
Namespace.DEFAULT_IMPORTS.deref(),
436434
)
437435
)
438436
)
439-
self._import_aliases: Atom[AliasMap] = Atom(lmap.Map.empty())
440-
self._interns: Atom[VarMap] = Atom(lmap.Map.empty())
441-
self._refers: Atom[VarMap] = Atom(lmap.Map.empty())
437+
self._import_aliases: AliasMap = lmap.Map.empty()
438+
self._interns: VarMap = lmap.Map.empty()
439+
self._refers: VarMap = lmap.Map.empty()
442440

443441
@classmethod
444442
def add_default_import(cls, module: str):
@@ -470,129 +468,133 @@ def module(self, m: BasilispModule):
470468
def aliases(self) -> NamespaceMap:
471469
"""A mapping between a symbolic alias and another Namespace. The
472470
fully qualified name of a namespace is also an alias for itself."""
473-
return self._aliases.deref()
471+
with self._lock:
472+
return self._aliases
474473

475474
@property
476475
def imports(self) -> ModuleMap:
477476
"""A mapping of names to Python modules imported into the current
478477
namespace."""
479-
return self._imports.deref()
478+
with self._lock:
479+
return self._imports
480480

481481
@property
482482
def import_aliases(self) -> AliasMap:
483483
"""A mapping of a symbolic alias and a Python module name."""
484-
return self._import_aliases.deref()
484+
with self._lock:
485+
return self._import_aliases
485486

486487
@property
487488
def interns(self) -> VarMap:
488489
"""A mapping between a symbolic name and a Var. The Var may point to
489490
code, data, or nothing, if it is unbound. Vars in `interns` are
490491
interned in _this_ namespace."""
491-
return self._interns.deref()
492+
with self._lock:
493+
return self._interns
492494

493495
@property
494496
def refers(self) -> VarMap:
495497
"""A mapping between a symbolic name and a Var. Vars in refers are
496498
interned in another namespace and are only referred to without an
497499
alias in this namespace."""
498-
return self._refers.deref()
500+
with self._lock:
501+
return self._refers
499502

500503
def __repr__(self):
501504
return f"{self._name}"
502505

503506
def __hash__(self):
504507
return hash(self._name)
505508

506-
def add_alias(self, alias: sym.Symbol, namespace: "Namespace") -> None:
507-
"""Add a Symbol alias for the given Namespace."""
508-
self._aliases.swap(lambda m: m.assoc(alias, namespace))
509+
def add_alias(self, namespace: "Namespace", *aliases: sym.Symbol) -> None:
510+
"""Add Symbol aliases for the given Namespace."""
511+
with self._lock:
512+
new_m = self._aliases
513+
for alias in aliases:
514+
new_m = new_m.assoc(alias, namespace)
515+
self._aliases = new_m
509516

510517
def get_alias(self, alias: sym.Symbol) -> "Optional[Namespace]":
511518
"""Get the Namespace aliased by Symbol or None if it does not exist."""
512-
return self.aliases.val_at(alias, None)
519+
with self._lock:
520+
return self._aliases.val_at(alias, None)
513521

514522
def remove_alias(self, alias: sym.Symbol) -> None:
515523
"""Remove the Namespace aliased by Symbol. Return None."""
516-
self._aliases.swap(lambda m: m.dissoc(alias))
524+
with self._lock:
525+
self._aliases = self._aliases.dissoc(alias)
517526

518527
def intern(self, sym: sym.Symbol, var: Var, force: bool = False) -> Var:
519528
"""Intern the Var given in this namespace mapped by the given Symbol.
520529
If the Symbol already maps to a Var, this method _will not overwrite_
521530
the existing Var mapping unless the force keyword argument is given
522531
and is True."""
523-
m: lmap.Map = self._interns.swap(Namespace._intern, sym, var, force=force)
524-
return m.val_at(sym)
525-
526-
@staticmethod
527-
def _intern(
528-
m: lmap.Map, sym: sym.Symbol, new_var: Var, force: bool = False
529-
) -> lmap.Map:
530-
"""Swap function used by intern to atomically intern a new variable in
531-
the symbol mapping for this Namespace."""
532-
var = m.val_at(sym, None)
533-
if var is None or force:
534-
return m.assoc(sym, new_var)
535-
return m
532+
with self._lock:
533+
old_var = self._interns.val_at(sym, None)
534+
if old_var is None or force:
535+
self._interns = self._interns.assoc(sym, var)
536+
return self._interns.val_at(sym)
536537

537538
def unmap(self, sym: sym.Symbol) -> None:
538-
self._interns.swap(lambda m: m.dissoc(sym))
539+
with self._lock:
540+
self._interns = self._interns.dissoc(sym)
539541

540542
def find(self, sym: sym.Symbol) -> Optional[Var]:
541543
"""Find Vars mapped by the given Symbol input or None if no Vars are
542544
mapped by that Symbol."""
543-
v = self.interns.val_at(sym, None)
544-
if v is None:
545-
return self.refers.val_at(sym, None)
546-
return v
545+
with self._lock:
546+
v = self._interns.val_at(sym, None)
547+
if v is None:
548+
return self._refers.val_at(sym, None)
549+
return v
547550

548551
def add_import(self, sym: sym.Symbol, module: Module, *aliases: sym.Symbol) -> None:
549552
"""Add the Symbol as an imported Symbol in this Namespace. If aliases are given,
550553
the aliases will be applied to the """
551-
self._imports.swap(lambda m: m.assoc(sym, module))
552-
if aliases:
553-
self._import_aliases.swap(
554-
lambda m: m.assoc(
555-
*itertools.chain.from_iterable([(alias, sym) for alias in aliases])
556-
)
557-
)
554+
with self._lock:
555+
self._imports = self._imports.assoc(sym, module)
556+
if aliases:
557+
m = self._import_aliases
558+
for alias in aliases:
559+
m = m.assoc(alias, sym)
560+
self._import_aliases = m
558561

559562
def get_import(self, sym: sym.Symbol) -> Optional[BasilispModule]:
560563
"""Return the module if a moduled named by sym has been imported into
561564
this Namespace, None otherwise.
562565
563566
First try to resolve a module directly with the given name. If no module
564567
can be resolved, attempt to resolve the module using import aliases."""
565-
mod = self.imports.val_at(sym, None)
566-
if mod is None:
567-
alias = self.import_aliases.get(sym, None)
568-
if alias is None:
569-
return None
570-
return self.imports.val_at(alias, None)
571-
return mod
568+
with self._lock:
569+
mod = self._imports.val_at(sym, None)
570+
if mod is None:
571+
alias = self._import_aliases.get(sym, None)
572+
if alias is None:
573+
return None
574+
return self._imports.val_at(alias, None)
575+
return mod
572576

573577
def add_refer(self, sym: sym.Symbol, var: Var) -> None:
574578
"""Refer var in this namespace under the name sym."""
575579
if not var.is_private:
576-
self._refers.swap(lambda s: s.assoc(sym, var))
580+
with self._lock:
581+
self._refers = self._refers.assoc(sym, var)
577582

578583
def get_refer(self, sym: sym.Symbol) -> Optional[Var]:
579584
"""Get the Var referred by Symbol or None if it does not exist."""
580-
return self.refers.val_at(sym, None)
585+
with self._lock:
586+
return self._refers.val_at(sym, None)
581587

582-
@classmethod
583-
def __refer_all(cls, refers: lmap.Map, other_ns_interns: lmap.Map) -> lmap.Map:
584-
"""Refer all _public_ interns from another namespace."""
585-
final_refers = refers
586-
for entry in other_ns_interns:
587-
s: sym.Symbol = entry.key
588-
var: Var = entry.value
589-
if not var.is_private:
590-
final_refers = final_refers.assoc(s, var)
591-
return final_refers
592-
593-
def refer_all(self, other_ns: "Namespace"):
588+
def refer_all(self, other_ns: "Namespace") -> None:
594589
"""Refer all the Vars in the other namespace."""
595-
self._refers.swap(Namespace.__refer_all, other_ns.interns)
590+
with self._lock:
591+
final_refers = self._refers
592+
for entry in other_ns.interns:
593+
s: sym.Symbol = entry.key
594+
var: Var = entry.value
595+
if not var.is_private:
596+
final_refers = final_refers.assoc(s, var)
597+
self._refers = final_refers
596598

597599
@classmethod
598600
def ns_cache(cls) -> lmap.Map:

src/basilisp/testrunner.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@
1313
from basilisp.lang.obj import lrepr
1414
from basilisp.util import Maybe
1515

16-
basilisp.init()
17-
importlib.import_module("basilisp.test")
18-
1916
_COLLECTED_TESTS_SYM = sym.symbol("collected-tests", ns="basilisp.test")
2017
_CURRENT_NS_SYM = sym.symbol("current-ns", ns="basilisp.test")
2118

2219

20+
# pylint: disable=unused-argument
21+
def pytest_configure(config):
22+
basilisp.init()
23+
importlib.import_module("basilisp.test")
24+
25+
2326
def pytest_collect_file(parent, path):
2427
"""Primary PyTest hook to identify Basilisp test files."""
2528
if path.ext == ".lpy":

tests/basilisp/compiler_test.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3123,8 +3123,8 @@ def test_aliased_var_does_not_resolve(
31233123
other_ns_name = sym.symbol("other.ns")
31243124
try:
31253125
other_ns = get_or_create_ns(other_ns_name)
3126-
current_ns.add_alias(other_ns_name, other_ns)
3127-
current_ns.add_alias(sym.symbol("other"), other_ns)
3126+
current_ns.add_alias(other_ns, other_ns_name)
3127+
current_ns.add_alias(other_ns, sym.symbol("other"))
31283128

31293129
with pytest.raises(compiler.CompilerException):
31303130
lcompile("(other/m :arg)")
@@ -3138,8 +3138,8 @@ def test_aliased_macro_symbol_resolution(
31383138
other_ns_name = sym.symbol("other.ns")
31393139
try:
31403140
other_ns = get_or_create_ns(other_ns_name)
3141-
current_ns.add_alias(other_ns_name, other_ns)
3142-
current_ns.add_alias(sym.symbol("other"), other_ns)
3141+
current_ns.add_alias(other_ns, other_ns_name)
3142+
current_ns.add_alias(other_ns, sym.symbol("other"))
31433143

31443144
with runtime.ns_bindings(other_ns_name.name):
31453145
lcompile("(def ^:macro m (fn* [&env &form v] v))")
@@ -3204,10 +3204,10 @@ def test_cross_ns_macro_symbol_resolution(
32043204
third_ns_name = sym.symbol("third.ns")
32053205
try:
32063206
other_ns = get_or_create_ns(other_ns_name)
3207-
current_ns.add_alias(other_ns_name, other_ns)
3207+
current_ns.add_alias(other_ns, other_ns_name)
32083208

32093209
third_ns = get_or_create_ns(third_ns_name)
3210-
other_ns.add_alias(third_ns_name, third_ns)
3210+
other_ns.add_alias(third_ns, third_ns_name)
32113211

32123212
with runtime.ns_bindings(third_ns_name.name):
32133213
lcompile(
@@ -3241,10 +3241,10 @@ def test_cross_ns_macro_symbol_resolution_with_aliases(
32413241
third_ns_name = sym.symbol("third.ns")
32423242
try:
32433243
other_ns = get_or_create_ns(other_ns_name)
3244-
current_ns.add_alias(other_ns_name, other_ns)
3244+
current_ns.add_alias(other_ns, other_ns_name)
32453245

32463246
third_ns = get_or_create_ns(third_ns_name)
3247-
other_ns.add_alias(sym.symbol("third"), third_ns)
3247+
other_ns.add_alias(third_ns, sym.symbol("third"))
32483248

32493249
with runtime.ns_bindings(third_ns_name.name):
32503250
lcompile(
@@ -3278,7 +3278,7 @@ def test_cross_ns_macro_symbol_resolution_with_refers(
32783278
third_ns_name = sym.symbol("third.ns")
32793279
try:
32803280
other_ns = get_or_create_ns(other_ns_name)
3281-
current_ns.add_alias(other_ns_name, other_ns)
3281+
current_ns.add_alias(other_ns, other_ns_name)
32823282

32833283
third_ns = get_or_create_ns(third_ns_name)
32843284

@@ -3313,8 +3313,8 @@ def other_ns(self, lcompile: CompileFn, ns: runtime.Namespace):
33133313
try:
33143314
other_ns = get_or_create_ns(other_ns_name)
33153315
Var.intern(other_ns_name, sym.symbol("m"), lambda x: x)
3316-
current_ns.add_alias(other_ns_name, other_ns)
3317-
current_ns.add_alias(sym.symbol("other"), other_ns)
3316+
current_ns.add_alias(other_ns, other_ns_name)
3317+
current_ns.add_alias(other_ns, sym.symbol("other"))
33183318

33193319
with runtime.ns_bindings(current_ns.name):
33203320
yield

tests/basilisp/namespace_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def test_alias(ns_cache: atom.Atom[NamespaceMap]):
277277
ns1 = get_or_create_ns(sym.symbol("ns1"))
278278
ns2 = get_or_create_ns(sym.symbol("ns2"))
279279

280-
ns1.add_alias(sym.symbol("n2"), ns2)
280+
ns1.add_alias(ns2, sym.symbol("n2"))
281281

282282
assert None is ns1.get_alias(sym.symbol("ns2"))
283283
assert ns2 is ns1.get_alias(sym.symbol("n2"))
@@ -301,10 +301,10 @@ def ns(self) -> Namespace:
301301
str_ns.intern(
302302
chars_sym, Var(ns, chars_sym, meta=lmap.map({kw.keyword("private"): True}))
303303
)
304-
ns.add_alias(str_ns_alias, str_ns)
304+
ns.add_alias(str_ns, str_ns_alias)
305305

306306
str_alias = sym.symbol("str")
307-
ns.add_alias(str_alias, Namespace(str_alias))
307+
ns.add_alias(Namespace(str_alias), str_alias)
308308

309309
str_sym = sym.symbol("str")
310310
ns.intern(str_sym, Var(ns, str_sym))

tests/basilisp/runtime_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ def test_resolve_alias(self, core_ns):
420420

421421
foo_ns_sym = sym.symbol("zux.bar.foo")
422422
foo_ns = get_or_create_ns(foo_ns_sym)
423-
ns.add_alias(sym.symbol("foo"), foo_ns)
423+
ns.add_alias(foo_ns, sym.symbol("foo"))
424424
assert sym.symbol(
425425
"aliased-var", ns=foo_ns_sym.name
426426
) == runtime.resolve_alias(sym.symbol("aliased-var", ns="foo"), ns=ns)

0 commit comments

Comments
 (0)