Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RAII #659

Open
wants to merge 74 commits into
base: master
Choose a base branch
from
Open

RAII #659

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
e248051
<ignore> added test artifacts to .ignore
hiemstar May 17, 2024
0e2cdb0
<feature> added __init metamethod to initialize managed types in raii.
hiemstar May 17, 2024
683526d
<feature> added __dtor metamethod to destruct managed variables in raii.
hiemstar May 17, 2024
5ba46c8
<feature> added __copy metamethod which enables specialized copy-assi…
hiemstar May 17, 2024
d867d28
<tests> added simple testing of raii metamethods __init, __dtor, __co…
hiemstar May 17, 2024
52ae95d
<terralib> revert commented out code to make things work on my local …
hiemstar May 17, 2024
31482a8
<bugfix> now calling __dtor before a new copy-assignment
hiemstar May 17, 2024
a685745
<bugfix> fixed dispatch of __copy. calling __copy is limited to rhs b…
hiemstar May 21, 2024
d677352
<refactor> added hasmetamethod(v, method). changed copy assignment be…
hiemstar May 21, 2024
b77f477
forgot to uncomment SDKROOT code on macos
renehiemstra May 22, 2024
8ba635e
<bugfix> fixed small bug in copyconstruction where the type is passed…
hiemstar May 22, 2024
1ff7038
<test> tests/raii-copyctr-cast.t, which combines __cast and __copy me…
hiemstar May 22, 2024
1d03783
Merge branch 'raii' of github.com:renehiemstra/terra into raii
hiemstar May 22, 2024
31a006b
<test> raii-shared_ptr.t which tests some functionality for a shared …
hiemstar May 22, 2024
ac0c68a
<bugfix> enabled copy assignments for rhs 'select' variables and rhs …
hiemstar May 23, 2024
8aee8b1
<test> raii-offset_ptr.t: example with a type that has some sematics …
hiemstar May 23, 2024
94caf67
<feature> added lib/terralibext.t that is being called from terralib …
hiemstar May 26, 2024
8cbc4b0
<refactor> changed __init, __copy, __dtor from metamethods to regular…
hiemstar May 26, 2024
509816d
<refactor> cleaned up terralibext.t
hiemstar May 27, 2024
0090b2e
<refactor> updated tests to incorporate changes in 'terralibext.t'
hiemstar May 27, 2024
9bf3f79
<test> raii-compose.t: testing composable use of managed structs.
hiemstar May 27, 2024
e1d9cf0
<feature> first implementation of raii composable managed datastructures
hiemstar May 27, 2024
49170c6
<refactor> updated tests/raii-shared_ptr.t
hiemstar May 27, 2024
2e3dcf3
<bugfix> fixed assignment__copy assignment. removed __dtor in custom …
hiemstar May 27, 2024
4e6bfc3
<refactor> reorganized raii tests.
hiemstar May 27, 2024
95a7e84
<test> raii-tuple-default-copy.t and fails/raii-tuple-custom-copy.t
hiemstar May 27, 2024
f4caa8b
<bugfix> fixed throwing error in createassignment in case of a tuple …
hiemstar May 27, 2024
25cb501
<bugfix> proper error exception for tuple assignment of managed varia…
hiemstar May 27, 2024
dd7754f
<uncomment> system code macos in terralib.lua
hiemstar May 27, 2024
5415fe2
<docs> lib/raii.md - discussing implementation and use of RAII concepts.
hiemstar May 29, 2024
ced17ed
<ignore> added test artifacts to .ignore
hiemstar May 17, 2024
8877111
<feature> added __init metamethod to initialize managed types in raii.
hiemstar May 17, 2024
b760837
<feature> added __dtor metamethod to destruct managed variables in raii.
hiemstar May 17, 2024
c503201
<feature> added __copy metamethod which enables specialized copy-assi…
hiemstar May 17, 2024
59d0945
<tests> added simple testing of raii metamethods __init, __dtor, __co…
hiemstar May 17, 2024
69b96e0
<terralib> revert commented out code to make things work on my local …
hiemstar May 17, 2024
5510a1d
<bugfix> now calling __dtor before a new copy-assignment
hiemstar May 17, 2024
010da09
<bugfix> fixed dispatch of __copy. calling __copy is limited to rhs b…
hiemstar May 21, 2024
4e363b0
<refactor> added hasmetamethod(v, method). changed copy assignment be…
hiemstar May 21, 2024
3dbede1
<bugfix> fixed small bug in copyconstruction where the type is passed…
hiemstar May 22, 2024
2c83bf9
<test> tests/raii-copyctr-cast.t, which combines __cast and __copy me…
hiemstar May 22, 2024
5e55ed5
forgot to uncomment SDKROOT code on macos
renehiemstra May 22, 2024
2c24f11
<test> raii-shared_ptr.t which tests some functionality for a shared …
hiemstar May 22, 2024
543bac5
<bugfix> enabled copy assignments for rhs 'select' variables and rhs …
hiemstar May 23, 2024
1b56cf1
<test> raii-offset_ptr.t: example with a type that has some sematics …
hiemstar May 23, 2024
9762db8
<feature> added lib/terralibext.t that is being called from terralib …
hiemstar May 26, 2024
44fa7ad
<refactor> changed __init, __copy, __dtor from metamethods to regular…
hiemstar May 26, 2024
a2671e3
<refactor> cleaned up terralibext.t
hiemstar May 27, 2024
0d21b6d
<refactor> updated tests to incorporate changes in 'terralibext.t'
hiemstar May 27, 2024
84dfe04
<test> raii-compose.t: testing composable use of managed structs.
hiemstar May 27, 2024
433c5b7
<feature> first implementation of raii composable managed datastructures
hiemstar May 27, 2024
93114ff
<refactor> updated tests/raii-shared_ptr.t
hiemstar May 27, 2024
880342e
<bugfix> fixed assignment__copy assignment. removed __dtor in custom …
hiemstar May 27, 2024
d2d2ce8
<refactor> reorganized raii tests.
hiemstar May 27, 2024
2c3a4e0
<test> raii-tuple-default-copy.t and fails/raii-tuple-custom-copy.t
hiemstar May 27, 2024
2aeb433
<bugfix> fixed throwing error in createassignment in case of a tuple …
hiemstar May 27, 2024
5fcd780
<bugfix> proper error exception for tuple assignment of managed varia…
hiemstar May 27, 2024
ee3b76d
<uncomment> system code macos in terralib.lua
hiemstar May 27, 2024
dd5efb3
<docs> lib/raii.md - discussing implementation and use of RAII concepts.
hiemstar May 29, 2024
19d2f39
<merge> rebased changes on top of upstream/master
hiemstar May 29, 2024
50abd4a
<refactor> addmissing<dtor,copy,init> generates a missing method only…
hiemstar May 29, 2024
ae58678
<refactor> prohibiting assignments of managed objects consisting of m…
hiemstar May 29, 2024
5c31ea5
<systemcode> forgot to uncomment system code for macos.
hiemstar May 29, 2024
ec311ad
<docs> updated /lib/raii.md
hiemstar May 29, 2024
35040c6
<docs> updated /lib/raii.md
hiemstar May 29, 2024
12f6128
Update terralib.lua
renehiemstra May 31, 2024
296ba5c
<docs> updated /lib/raii.md
hiemstar Jun 5, 2024
e020696
<BUILD> added terralibext.t to list of terra files that are to be ins…
hiemstar Jun 27, 2024
df66d4c
Merge remote-tracking branch 'upstream/master' into raii
hiemstar Jun 27, 2024
84ebde2
Merge branch 'terralang:master' into raii
renehiemstra Aug 13, 2024
eaff090
<bugfix> fixed issue with assignment of managed variables where a tem…
hiemstar Aug 27, 2024
487cf69
<refactor> removed the print statements in terralibext.t in the gener…
hiemstar Aug 27, 2024
8109b94
Merge branch 'raii' of github.com:renehiemstra/terra into raii
hiemstar Aug 27, 2024
477ba23
<bugfix> fixed pasted-copy bug in __addmissingcopy - where hasdtor sh…
hiemstar Sep 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/build
/cmake-build-debug
/docs/_site
/docs/_vendor
/install
Expand All @@ -18,6 +19,13 @@
/tests/dynlib
/tests/dynlib.exe
/tests/not_bc
/tests/class2
/tests/inline_c.exe
/tests/objc
/tests/objc2
/tests/stdio.exe

/.idea

*.bc
*.ll
Expand Down
152 changes: 152 additions & 0 deletions lib/raii.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# RAII - Resource management
Resource acquisition is initialization ([RAII](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)) provides a deterministic means of safe resource management. It is generally associated with systems programming languages such as *c++* and *rust*.

In the following I summarize the experimental implementation that you can find [here](https://github.com/renehiemstra/terra/tree/raii). The socalled [Big Three](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)) are supported:
* object destruction
* copy assignment
* copy construction

Terra does not support rvalue references (introduced e.g. in *c++11*), so the experimental RAII support is comparable to that of *C++03* or *rust*.

## Feature summary
Compiler support for the following methods:
```
A.methods.__init(self : &A)
A.methods.__dtor(self : &A)
(A or B).methods.__copy(from : &A, to : &B)
```
These methods support the implementation of smart containers and smart pointers, like *std::string*, *std::vector* and *std::unique_ptr*, *std::shared_ptr*, *boost:offset_ptr* in C++.

The design does not introduce any breaking changes. No new keywords are introduced. Heap resources are acquired and released using the regular *C stdlib* functions such as malloc and free, leaving memory allocation in the hands of the programmer.

If implemented, these methods are inserted judiciously during the type checking phase, implemented in *terralib.lua*. All these metamethods can be implemented as macro's or as terra functions.

## Compiler supported methods for RAII
A managed type is one that implements at least `__dtor` and optionally `__init` and `__copy` or, by induction, has fields or subfields that are of a managed type. In the following I assume `struct A` is a managed type.

To enable RAII, import the library */lib/terralibext.t* using
```
require "terralibext"
```
The compiler only checks for `__init`, `__dtor` and `__copy` in case this libreary is loaded.

### Object initialization
`__init` is used to initialize managed variables:
```
A.methods.__init(self : &A)
```
The compiler checks for an `__init` method in any variable definition statement, without explicit initializer, and emits the call right after the variable definition, e.g.
```
var a : A
a:__init() --generated by compiler
```
### Copy assignment
`__copy` enables specialized copy-assignment and, combined with `__init`, copy construction. `__copy` takes two arguments, which can be different, as long as one of them is a managed type, e.g.
```
A.metamethods.__copy(from : &A, to : &B)
```
and / or
```
A.metamethods.__copy(from : &B, to : &A)
```
If `a : A` is a managed type, then the compiler will replace a regular assignment by a call to the implemented `__copy` method
```
b = a ----> A.methods.__copy(a, b)
```
or
```
a = b ----> A.methods.__copy(b, a)
```
`__copy` can be a (overloaded) terra function or a macro.

The programmer is responsable for managing any heap resources associated with the arguments of the `__copy` method.

### Copy construction
In object construction, `__copy` is combined with `__init` to perform copy construction. For example,
```
var b : B = a
```
is replaced by the following statements
```
var b : B
b:__init() --generated by compiler if `__init` is implemented
A.methods.__copy(a, b) --generated by compiler
```
If the right `__copy` method is not implemented but a user defined `__cast` metamethod exists that can cast one of the arguments to the correct type, then the cast is performed and then the relevant copy method is applied.

### Object destruction
`__dtor` can be used to free heap memory
```
A.methods.__dtor(self : &A)
```
The implementation adds a deferred call to `__dtor ` near the end of a scope, right before a potential return statement, for all variables local to the current scope that are not returned. Hence, `__dtor` is tied to the lifetime of the object. For example, for a block of code the compiler would generate
```
do
var x : A, y : A
...
...
defer x:__dtor() --generated by compiler
defer y:__dtor() --generated by compiler
end
```
or in case of a terra function
```
terra foo(x : A)
var y : A, z : A
...
...
defer z:__dtor() --generated by compiler
return y
end
```
`__dtor` is also called before any regular assignment (if a __copy method is not implemented) to free 'old' resources. So
```
a = b
```
is replaced by
```
a:__dtor() --generated by compiler
a = b
```
## Compositional API's
If a struct has fields or subfields that are managed types, but do not implement `__init`, `__copy` or `__dtor`, then the compiler will generate default methods that inductively call existing `__init`, `__copy` or `__dtor` methods for its fields and subfields. This enables compositional API's like `vector(vector(int))` or `vector(string)`. This is implemented as an extension to *terralib.lua* in *lib/terralibext.t*.

## Examples
The following files have been added to the terra testsuite:
* *raii.t* tests whether `__dtor`, `__init`, and `__copy` are evaluated correctly for simple datatypes.
* *raii-copyctr-cast.t* tests the combination of `metamethods.__cast` and `__copy`.
* *raii-unique_ptr.t* tests some functionality of a unique pointer type.
* *raii-shared_ptr.t* tests some functionality of a shared pointer type.
* *raii-offset_ptr.t* tests some functionality of an offset pointer implementation, found e.g. in *boost.cpp*.
* *raii-compose.t* tests the compositional aspect.

You can have a look there for some common code patterns.

## Current limitations
* The implementation is not aware of when an actual heap allocation is made and therefore assumes that a managed variable always carries a heap resource. It is up to the programmer to properly initialize pointer variables to nil to avoid calling 'free' on uninitialized pointers.
* Tuple (copy) assignment (regular or using `__copy`) are prohibited by the compiler in case of managed variables. This is done to prevent memory leaks or unwanted deletions in assignments such as
```
a, b = b, a
```
* Currently, there is no way to prevent unwanted calls to `__dtor` in cases such as the following. Consider
```
terra foo()
var b : A
return bar(b)
end
```
which will get expanded to
```
terra foo()
var b : A
defer b:__dtor() --generated by compiler
return bar(b)
end
```
If `bar` would return `b` then its associated heap resources would be released before they can be used in the outer scope.

## Roadmap
The current implementation already works in a range of important applications, as can be seen in the tests and the examples above. To remove the noted limitations and to enable graceful compile-time errors my plan is to:
* support *affine types* (similar to *rust*) by checking, at compile time, that a managed variable is used (passed by value) not more than once. This essentially means that the variable is moved from (not copied) on every use. This is not restrictive, since in general you would pass managed objects by reference, not by value.
* support borrow checking (similar to *rust*) by counting, at compile time, the number of references.
* introduce a `__new` method that signals a heap allocation. This way the compiler is made aware of all heap allocations being made.
221 changes: 221 additions & 0 deletions lib/terralibext.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
--local io = terralib.includec("stdio.h")

local function addmissinginit(T)

--flag that signals that a missing __init method needs to
--be generated
local generate = false

local runinit = macro(function(self)
local V = self:gettype()
--avoid generating code for empty array initializers
local function hasinit(U)
if U:isstruct() then return U.methods.__init
elseif U:isarray() then return hasinit(U.type)
else return false end
end
if V:isstruct() then
if not V.methods.__init then
addmissinginit(V)
end
local method = V.methods.__init
if method then
generate = true
return quote
self:__init()
end
end
elseif V:isarray() and hasinit(V) then
return quote
var pa = &self
for i = 0,T.N do
runinit((@pa)[i])
end
end
elseif V:ispointer() then
return quote
self = nil
end
end
return quote end
end)

local generateinit = macro(function(self)
local T = self:gettype()
local stmts = terralib.newlist()
local entries = T:getentries()
for i,e in ipairs(entries) do
if e.field then
local expr = `runinit(self.[e.field])
if expr and #expr.tree.statements > 0 then
stmts:insert(expr)
end
end
end
return stmts
end)

if T:isstruct() and T.methods.__init == nil then
local method = terra(self : &T)
generateinit(@self)
--io.printf("generated __init()\n")
end
if generate then
T.methods.__init = method
else
--set T.methods.__init to false. This means that addmissinginit(T) will not
--attempt to generate 'T.methods.__init' twice
T.methods.__init = false
end
end
end


local function addmissingdtor(T)

--flag that signals that a missing __dtor method needs to
--be generated
local generate = false

local rundtor = macro(function(self)
local V = self:gettype()
--avoid generating code for empty array destructors
local function hasdtor(U)
if U:isstruct() then return U.methods.__dtor
elseif U:isarray() then return hasdtor(U.type)
else return false end
end
if V:isstruct() then
if not V.methods.__dtor then
addmissingdtor(V)
end
local method = V.methods.__dtor
if method then
generate = true
return quote
self:__dtor()
end
end
elseif V:isarray() and hasdtor(V) then
return quote
var pa = &self
for i = 0,T.N do
rundtor((@pa)[i])
end
end
end
return quote end
end)

local generatedtor = macro(function(self)
local T = self:gettype()
local stmts = terralib.newlist()
local entries = T:getentries()
for i,e in ipairs(entries) do
if e.field then
local expr = `rundtor(self.[e.field])
if expr and #expr.tree.statements > 0 then
stmts:insert(expr)
end
end
end
return stmts
end)

if T:isstruct() and T.methods.__dtor==nil then
local method = terra(self : &T)
--io.printf("generated __dtor()\n")
generatedtor(@self)
end
if generate then
T.methods.__dtor = method
else
--set T.methods.__dtor to false. This means that addmissingdtor(T) will not
--attempt to generate 'T.methods.__dtor' twice
T.methods.__dtor = false
end
end
end

local function addmissingcopy(T)

--flag that signals that a missing __copy method needs to
--be generated
local generate = false

local runcopy = macro(function(from, to)
local U = from:gettype()
local V = to:gettype()
--avoid generating code for empty array initializers
local function hascopy(W)
if W:isstruct() then return W.methods.__copy
elseif W:isarray() then return hascopy(W.type)
else return false end
end
if V:isstruct() and U==V then
if not V.methods.__copy then
addmissingcopy(V)
end
local method = V.methods.__copy
if method then
generate = true
return quote
method(&from, &to)
end
else
return quote
to = from
end
end
elseif V:isarray() and hascopy(V) then
return quote
var pa = &self
for i = 0,V.N do
runcopy((@pa)[i])
end
end
else
return quote
to = from
end
end
return quote end
end)

local generatecopy = macro(function(from, to)
local stmts = terralib.newlist()
local entries = T:getentries()
for i,e in ipairs(entries) do
local field = e.field
if field then
local expr = `runcopy(from.[field], to.[field])
if expr and #expr.tree.statements > 0 then
stmts:insert(expr)
end
end
end
return stmts
end)

if T:isstruct() and T.methods.__copy==nil then
local method = terra(from : &T, to : &T)
generatecopy(@from, @to)
--io.printf("generated __copy()\n")
end
if generate then
T.methods.__copy = method
else
--set T.methods.__copy to false. This means that addmissingcopy(T) will not
--attempt to generate 'T.methods.__copy' twice
T.methods.__copy = false
end
end
end

terralib.ext = {
addmissing = {
__init = addmissinginit,
__dtor = addmissingdtor,
__copy = addmissingcopy
}
}
Loading
Loading