summaryrefslogtreecommitdiff
path: root/notes/arena_allocation.txt
blob: aeb12bd645a4a2422a89f3d388b2f681d28202e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

arena allocation with staging
-----------------------------

    ref Node n = staging_alloc(ctx.arena)
    check n.header = parse_header(ctx),   # these will allocate inside the staging area
          n.body = parse_body(ctx) {
        staging_free(n)
        return none
    }
    return staging_commit(n)  # otherwise we will leak staging areas!
    return n     # ^ can we move the data?
                 # yes, if:
                 #  - we do not use arena-internal pointers (unlikely to work)
                 #  - we use relative pointers for (and only for) arena-internal pointers
                 #  - we use some kind of meta data to track and update arena-internal pointers
                 #  - all possibly allocated data in the arena has public structure definitions,
                 #    and code is generated after the staging_commit call to update those



This would require some kind of new "own" keyword to be safe....

    # the default "refstate" is memory, which is built in (and perhaps should not even have an identifier)
    refstate staging;
    func <T>staging_alloc(rwref Arena, size = sizeof T) returns ownref[staging] T
    func <T>staging_free(ownref[staging] T)
    func <T>staging_commit(ownref[staging] T) return ownref T

How to enforce that no refs point to inside the staged arena after a free? Perhaps
add a new function for tracking this?

    func <T>staging_track_incoming(rwref StagedSegment segment, rwref addr? T ref_to_arena) # ... but how to remove the ref safely? can we just set it to null?
    func <T>staging_track_incoming(rwref StagedSegment segment, rwref addr? T ref_to_arena, addr? orig_addr) # ... better but still cannot do things like removing tree/list entries etc.

We need to handle these two cases:

    ownref T a = ...;
    a.x = b.y         # 1. Outgoing pointer
    c.z = a.b         # 2. (a.b is owned by a) Incoming pointer


alternative approach
--------------------
An alternative approach is to take ownership of the arena (so no more allocations can happen),
save the current arena free pointer, and then simply restore the free pointer value.

With this approach, the arena works a like a stack with regards to staging:

   +--------+-----------------------+-----------------+----------+----------+
   | Header | Arena data, committed | Staged, level 1 | Staged 2 |  unused  |
   +--------+-----------------------+-----------------+----------+----------+
                                                                 ^
                                                                 \ free pointer

Staged "segments" can only be free'd as a whole (because the free pointer is changed).

If the arena is splitted into multiple blocks, we need to take that into account
as well (i.e. the staged segment may span multiple blocks). When freeing a segment
that spans multiple blocks, all but the last blocks become unused and can be free'd.



With this approach, we need to be able to allow outgoing refs from the
staged segment to earlier segments of the arena, but deny incoming refs
from earlier segments to the staged arena (or track them somehow, but in
that case then modification/copying of the ref must be forbidden).