Skip to content

Commit 4feb220

Browse files
Add documentation for developers
1 parent 5cce7e1 commit 4feb220

File tree

1 file changed

+44
-2
lines changed

1 file changed

+44
-2
lines changed

docs/DevelopersDocumentation.rst

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,12 +423,54 @@ library files and run pytest:
423423
python -m pip install pytest
424424
python -m pytest -sv
425425
426-
***********************************
426+
###################################
427427
CppInterOp Internal Documentation
428-
***********************************
428+
###################################
429429

430430
CppInterOp maintains an internal Doxygen documentation of its components.
431431
Internal documentation aims to capture intrinsic details and overall usage of
432432
code components. The goal of internal documentation is to make the codebase
433433
easier to understand for the new developers. Internal documentation can be
434434
visited : `here <build/html/index.html>`_
435+
436+
**************************************
437+
Multiple Interpreter & Thread-Safety
438+
**************************************
439+
440+
CppInterOp allows the user to create multiple interpreters at a time and
441+
use those interpreters. The interpreters that are created are stored in a
442+
stack and a map. The stack is used to enable the model where the user
443+
wants to create a temporary interpreter and destroy it after performing a
444+
few operations. In such a use case, the top of the stack is the only
445+
interpreter in use at any given point in time.
446+
447+
The map is used to store the mapping from :code:`clang::ASTContext` to
448+
:code:`Cpp::InterpreterInfo`. This is required to figure out which
449+
interpreter an object belongs to. Say the library user performs the
450+
following operations:
451+
452+
1. Create an Interpreter
453+
2. Compile some code with variable :code:`a`
454+
3. Create another Interpreter
455+
4. Performs :code:`Cpp::GetVariableOffset(a)`
456+
457+
In step 4, the top of the stack is an interpreter without the definition of
458+
:code:`a`. And we cannot use it to figure out the address of :code:`a`.
459+
The :code:`clang::Decl` passed to :code:`Cpp::GetVariableOffset` is used to
460+
retrieve the :code:`clang::ASTContext`, using
461+
:code:`clang::Decl::getASTContext`. We then use the map to figure out the
462+
exact Interpreter Instance this :code:`clang::Decl` belongs to and perform
463+
the operation.
464+
465+
A shortcoming of this is that if the CppInterOp accepts a
466+
:code:`clang::QualType` instead of :code:`clang::Decl`, then it is not
467+
possible to get the :code:`clang::ASTContext` from the :code:`clang::QualType`.
468+
In such cases, we iterate over the Allocator of all the Interpreters in our
469+
stack and figure out which :code:`clang::ASTContext` allocated this
470+
:code:`clang::QualType`. This is a very expensive operation. But there is no
471+
alternative to this.
472+
473+
For **thread-safety**, we introduce a lock for each of the interpreters we
474+
create. And lock only that one specific interpreter when required. We also
475+
have 2 global locks, one for LLVM, and another is used to lock operations
476+
performed on the interpreter stack and the map itself.

0 commit comments

Comments
 (0)