Skip to content

Commit

Permalink
Merge pull request #39 from sourceryinstitute/smart-pointers
Browse files Browse the repository at this point in the history
More clear, descriptive, and safe nomenclature
  • Loading branch information
rouson authored Sep 23, 2022
2 parents 974ebeb + 26e1718 commit c08cab3
Show file tree
Hide file tree
Showing 17 changed files with 265 additions and 245 deletions.
114 changes: 61 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,74 +1,75 @@
Reference Counter
=================
Smart Pointers
==============

```
__________ _____
\______ \ _____/ ____\___________ ____ ____ ____ ____
| _// __ \ __\/ __ \_ __ \_/ __ \ / \_/ ___\/ __ \
| | \ ___/| | \ ___/| | \/\ ___/| | \ \__\ ___/
|____|_ /\___> |__| \___> |__| \___> |___| /\____>\___>
\/
_________ __
\_ ___ \ ____ __ __ _____/ |_ ___________
/ \ \/ / _ \| | \/ \ __\/ __ \_ __ \
\ \___( <_> ) | / | \ | \ ___/| | \/
\_______/ \____/|____/|___| /__| \___> __|
_________ __
/ _____/ _____ _____ ________/ |_
\_____ \ / \\__ \\_ __ \ __\
/ \ Y Y \/ __ \| | \/| |
/_______ /__|_| (____ /__| |__|
\/ \/ \/
__________ .__ __
\______ \____ |__| _____/ |_ ___________ ______
| ___/ _ \| |/ \ __\/ __ \_ __ \/ ___/
| | ( <_> ) | | \ | \ ___/| | \/\___ \
|____| \____/|__|___| /__| \___ >__| /____ >
\/ \/ \/
\/
```

Overview
--------
Reference Counter offers semi-automatic counting of references to program resources
such as memory. A user accesses the provided reference-counting capability simply
by defining a non-abstract derived type that
The Smart-Pointers library tracks references to program resources and automates
the freeing of those resources if and only if the reference count drops to zero.
Most commonly, the reference is a pointer and the resource is memory. In that
context, Smart-Pointers help to prevent memory leaks and dangling pointers, which
commonly causes programs to crash due to memory limitations or segmentation faults,
respectively.

1. Extends Reference Counter's `ref_reference_t` type and
2. Implements the so-inherited `free` deferred binding.
To use Smart-Pointers, define a non-abstract derived type that

Because the reference-counting algorithm involves copying references in certain
circumstances, the user type that extends `ref_reference_t` should be a lightweight
proxy for a more stateful entity stored elsewhere. For example, the user type might
contain a Fortran `pointer` associated with some other object or it might contain
a "shadow" object that serves as an identity tag for a larger object allocated
(and later freed) in C or C++ at the direction of the user's `free` procedure.
1. Extends Smart Pointer's `sp_smart_pointer_t` type,
2. Implements the inherited `free` deferred binding, and
3. Invokes the inherited `start_count` procedure inside object constructors.

You can then use intrinsic assignments to copy instances of a `sp_smart_pointer_t`
child type, resulting in a [shallow copy] with the advantage that the target
will be finalized only when it becomes safe to do so.

Example
-------
See the [example](./example) folder for a demonstration of the use of Smart-Pointers.

Background
----------

For more background on the design philosophy and the internal mechanics of Reference
Counter, see Rouson et al. (see [[1]], [[2]], [[3]]). This repository's code
originated from refactoring the code in those publications to use more descriptive
and more general nomenclature and more up-to-date coding conventions. For example,
this repository separates interface bodies into modules and procedure definitions
into submodules.
more up-to-date coding conventions. For example, this repository separates interface
bodies into modules and procedure definitions into submodules. This repository also
uses more descriptive nomenclature for the types and procedures.

As compared to the original code, this repository also adds
This repository also adds
1. A [Fortran Package Manager] build system,
2. Tests based on the [Vegetables] unit-testing software,
2. Tests based on the [Veggies] unit-testing framework,
3. Documentation generated by [`ford`] and deployed to the web via GitHub Actions, and
4. Quality control via continuous integration testing using GitHub Actions.

Documentation
-------------
See [Reference Counter's GitHub Pages site] for HTML documentation generated with [`ford`].

See the [doc/] subdirectory for a [PlantUML] script that generates the Unified Modeling Langauge (UML) class diagram below of the three derived types in reference-counter.

![class_diagram](https://user-images.githubusercontent.com/13108868/165135689-4d2e85fe-6946-472f-a154-aaabebf6d4f5.png)

The above image was created with the PlantuML package in the [Atom] editor.


Compiler Status
---------------
Supported Compilers
-------------------
Correct execution of the Reference Counter library code requires comprehensive
compiler support for Fortran's type finalization semantics. The unit test suite
includes compiler standard-conformance tests. We have attempted to include at least
one example of each scenario in which the Fortran 2018 standard requires compilers
to finalize objects. The table below summarizes the observed compiler behaviors:
includes compiler standard-conformance tests that include a test for each scenario
in which the Fortran 2018 standard requires that an object be finalized.
The table below summarizes the observed compiler behaviors:

| _Compiler_ | _Test failures_ | _Version tested_ |
| :--- | :---: | :--- |
| NAG | 0 | `nagfor` 7.1 Build 7113 |
| GCC | 6 | `gfortran` 12.2.0 |
| Intel | 2 | `ifort` 2021.5.0 Build 20211109\_000000 |
| Cray | 3 | `ftn` 13.0.1 |
| GCC | 6 | `gfortran` 12.2.0 |
| NVIDIA | Fails to build (ICE) | `nvfortran` 2022.2 |
| AMD | Fails to build (ICE) | `flang` 13.0.0 (AOCC_3.2.0-Build\#128 2021\_11\_12) |

Expand All @@ -80,19 +81,19 @@ Downloading, Building, and Testing
On Linux, macOS, or Windows Subsystem for Linux, download, build, and test with
the following shell commands:
```
git clone git@github.com:sourceryinstitute/reference-counter
cd reference-counter
git clone https://github.com/sourceryinstitute/smart-pointer
cd smart-pointer
```
followed by one of the commands below depending on your compiler choice.
followed by one of the commands below corresponding to your compiler choice.

### GCC (`gfortran`)
### Numerical Algorithms Group (`nagfor`)
```
fpm test
fpm test --compiler nagfor --flag -fpp
```

### Numerical Algorithms Group (`nagfor`)
### GCC (`gfortran`)
```
fpm test --compiler nagfor --flag -fpp
fpm test
```

### Intel (`ifort`)
Expand All @@ -110,6 +111,12 @@ fpm test --compiler nvfortran --flag -Mpreprocess
fpm test --compiler flang --flag -cpp
```

Documentation
-------------
See [Reference Counter's GitHub Pages site] for HTML documentation generated with [`ford`].
See the [doc/] subdirectory for a [PlantUML] script that generates the Unified Modeling Langauge (UML)
class diagram below of the three derived types in reference-counter.

[1]: https://doi.org/10.1016/j.procs.2010.04.166
[2]: https://doi.org/10.1017/cbo9780511977381
[3]: https://doi.org/10.1109/MCSE.2012.33
Expand All @@ -120,3 +127,4 @@ fpm test --compiler flang --flag -cpp
[Atom]: https://atom.io
[PlantUML]: https://plantuml.com
[doc/]: ./doc
[shallow copy]: https://en.wikipedia.org/wiki/Object_copying#Shallow_copy
12 changes: 12 additions & 0 deletions doc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Classes
-------

The Unified Modeling Language (UML) class diagram below depicts the classes
in the Smart-Pointer library. Non-abstract user-defined derived types that
extend `sp_smart_pointer_t` inherit an obligation to define the `free` deferred
binding according to the `free_interface` abstract interface defined in
`sp_resource_m`. The user-defined `free` subroutine must free the associated
resource, which usually means deallocating the associated memory.

![class_diagram](https://user-images.githubusercontent.com/13108868/165135689-4d2e85fe-6946-472f-a154-aaabebf6d4f5.png)

85 changes: 42 additions & 43 deletions example/smart_pointer.f90
Original file line number Diff line number Diff line change
@@ -1,34 +1,25 @@
module foo_m
module user_object_m
use smart_pointer_m, only: sp_smart_pointer_t
implicit none

private
public :: foo_t
public :: user_object_t, user_object_ptr_t

type foo_t
type user_object_t
end type

end module

module smart_pointer_m
use reference_counter_m, only: ref_reference_t
use foo_m, only : foo_t

implicit none
private
public :: smart_pointer_t

type, extends(ref_reference_t) :: smart_pointer_t
type(foo_t), pointer :: ref => null()
type, extends(sp_smart_pointer_t) :: user_object_ptr_t
type(user_object_t), pointer :: ref => null()
contains
procedure :: free
end type

interface smart_pointer_t
interface user_object_ptr_t

module function construct(foo) result(smart_pointer)
module function construct(user_object) result(user_object_ptr)
implicit none
type(foo_t), intent(in), pointer:: foo
type(smart_pointer_t) :: smart_pointer
type(user_object_t), intent(in), pointer:: user_object
type(user_object_ptr_t) :: user_object_ptr
end function

end interface
Expand All @@ -37,65 +28,73 @@ module function construct(foo) result(smart_pointer)

module subroutine free(self)
implicit none
class(smart_pointer_t), intent(inout) :: self
class(user_object_ptr_t), intent(inout) :: self
end subroutine

end interface

end module

submodule(smart_pointer_m) smart_pointer_s
submodule(user_object_m) user_object_ptr_s
use assert_m, only : assert
implicit none

contains

module procedure construct
call assert(associated(foo), "construct_from_pointer: associated(foo)")
smart_pointer%ref => foo
call smart_pointer%start_ref_counter
call assert(associated(user_object), "construct_from_pointer: associated(user_object)")
user_object_ptr%ref => user_object
call user_object_ptr%start_counter
end procedure

module procedure free
if (associated(self%ref)) then
deallocate(self%ref)
nullify(self%ref)
print *,"free(): foo deallocated"
print *,"free(): user_object deallocated"
end if
end procedure

end submodule

program main
use smart_pointer_m, only : smart_pointer_t
use foo_m, only : foo_t
use user_object_m, only : user_object_t, user_object_ptr_t
use assert_m, only : assert
implicit none

block
type(user_object_ptr_t) smart_pointer_1, smart_pointer_2
type(user_object_t), pointer :: user_object => null()

type(smart_pointer_t) ptr_1, ptr_2
type(foo_t), pointer :: foo => null()
print *, "Allocating user_object pointer."
allocate(user_object, source = user_object_t())

allocate(foo, source = foo_t())
ptr_1 = smart_pointer_t(foo) ! 1st reference
print *, ptr_1%reference_count()
ptr_2 = ptr_1 ! 2nd reference
print *, ptr_2%reference_count()
call new_reference(ptr_2)
print *, ptr_2%reference_count() ! 2 remaining references
print *, "Defining smart_pointer_1."
smart_pointer_1 = user_object_ptr_t(user_object)
print *, "Reference count = ", smart_pointer_1%reference_count()
print *, "Copying smart_pointer_1 into smart_pointer_2."

end block ! ref_reference_counter frees the memory after the 2 remaining references go out of scope
smart_pointer_2 = smart_pointer_1
print *, "Reference count = ", smart_pointer_1%reference_count()
call assert(smart_pointer_1%reference_count()==smart_pointer_2%reference_count(), "consistent counts")

print *,"All references gone"
call new_reference(smart_pointer_2)
call assert(smart_pointer_1%reference_count()==smart_pointer_2%reference_count(), "consistent counts")
print *, "Reference count = ", smart_pointer_1%reference_count()
print *, "smart_pointer_1 and smart_pointer_2 going out of scope"
end block

contains

subroutine new_reference(obj)
type(smart_pointer_t), intent(in) :: obj
type(smart_pointer_t) local_ptr
subroutine new_reference(dummy_argument)
type(user_object_ptr_t), intent(in) :: dummy_argument
type(user_object_ptr_t) smart_pointer_3

local_ptr = obj ! 3rd reference
print *, local_ptr%reference_count()
print *, "Copying smart_pointer_2 into smart_pointer_3."
smart_pointer_3 = dummy_argument
call assert(dummy_argument%reference_count()==smart_pointer_3%reference_count(), "consistent counts")
print *, "Reference count = ", smart_pointer_3%reference_count()
print *, "smart_pointer_3 going out of scope."
end subroutine

end program
64 changes: 0 additions & 64 deletions src/reference_counter/ref_counter_m.f90

This file was deleted.

Loading

0 comments on commit c08cab3

Please sign in to comment.