12 Memory
12.1 Overview
(1) All the storage of a state is in the block the host gave (5.1) and is placed at load, apart from memory the host lends and vouches for (12.1 (2)). No storage appears while running. There is no garbage collector, and no value refers to another by address.
(2) Values lie in four kinds of places:
- declared storage (clause 8): globals, arrays, lists, queues, pools, buffers; their declarations fix their size;
- slots of the stack and of locals; their depth is fixed at load (9.3);
- one region for each task (12.2), where the bytes of text made while running go;
- memory the host gives: bound and shared storage (8.12, 8.13), the strings it passes to a run and those a
KEEP_STRhost word gives (19.3). The host keeps it alive and unchanged while the state reads it.
(3) So the upper bound of the memory a program uses is known at load. Using up the region or a capacity shows as a fault or a failure while running; it never leads the processor to ask for more memory.
12.2 The region and marks
(1) Words that make new bytes — cat, str.upper, the words that turn numbers into text and so on — write the result into the region and give a view of it. The region is filled from the front. The word region_free gives the number of bytes left.
(2) mark gives the current place in the region as an owned value (type mark). mark release rewinds the region to that place; every byte made after the mark can be used again. A loop that makes much text marks and releases in every turn.
(3) Bytes put into stored text (str(N)) are copied there, so they survive rewinding the region.
12-region.pnx
use io
use str
global kept str(32)
word line (i int gives str) cat ("line number ", i) end
word demo ()
region_free to start
1 line to a rem a view of 13 new bytes in the region
a set kept rem stored text: the bytes are copied out
start region_free sub to one
mark to m
2 1000 range do (i int) i line drop end each
start region_free sub to many
m release rem back to the place of the mark
start region_free sub to after
cat ("one line: ", one, ", 999 lines: ", many, ", after release: ", after) io.println
kept io.println
end
demo
Output
$ pnx 12-region.pnx
one line: 13, 999 lines: 14877, after release: 13
line number 1
(4) A str result the host receives can be used only until the next call on that state. Text the host keeps longer it copies into its own memory (clause 19).
Faults
(5) A region too small is the fault full.
12-region-full.pnx
word fill ()
loop cat ("this line takes some bytes of the region ", 0) drop end
end
fill
Fault
$ pnx 12-region-full.pnx
12-region-full.pnx:2: a declared capacity is used up
12.3 The life of views
(1) A view into the region cannot be used once its bytes are rewound. A processor checks this at load. Within one body (a word, a block value, the code outside definitions) it counts, for each str value, the marks made before it. mark release takes back the values made after that mark.
Constraints
(2) A release while a value it takes back is still on the stack is refused.
12-release-stack.pnx
word f (gives str)
mark to m
cat ("a", "b")
m release
end
Refused
$ pnx 12-release-stack.pnx
12-release-stack.pnx:4:5: a str made after this mark is still on the stack; the release gives its bytes back
(2a) A release in deferred code that runs because the word fails (or_fail, fail; 10.7 (1)) does not count the values the word leaves on the stack: the word ends there, its stack is not read again, and deferred code takes nothing from it (10.7 (4)). The values of the deferred code itself count as in (2), and a local holding a value taken back is as in (3). A failure carries only its code (11.2), so no str leaves the word that way.
12-release-failing.pnx
use io
use str
word check (w str or fail)
w str.len 5 gt if err.range fail end
end
word words_of (line str or fail)
loop
mark to m
defer m release end
line str.lower str.words do (w str) w check or_fail w io.println end each
break
end
end
"Pinax interprets" words_of or_else (e) cat ("failed: ", e err_text) io.println end
Output
$ pnx 12-release-failing.pnx
pinax
failed: range
(3) A local holding a value taken back cannot be read until something is stored into it again.
12-release-view.pnx
use io
word f ()
mark to m
cat ("made after ", "the mark") to s
m release
s io.println
end
Refused
$ pnx 12-release-view.pnx
12-release-view.pnx:6:3: this str was made after a mark that was released since; the region may hold other bytes there now
(4) The check leans to caution. String literals are not in the region and are never taken back. Every other str — views, stored text read back, values words give — counts as made where it appears. Where paths meet, a value counts as the latest and a mark as the earliest. So it may refuse a correct program; then keep the value in a str(N) or move the mark.
(5) Handing an owned mark to a word counts as a release there. A word that takes a mark counts its str inputs as made after that mark. Two tasks sharing one region are not followed by this check (clause 14).
12.4 Owned values
(1) owned before a type means an owned value: an open file and other handles, a row taken from a pool, a mark of the region. A processor checks at load that such a value is handed over exactly once on every path.
(2) An owned value is used in two ways:
- borrowing: giving it to a word that takes the type without
owned(f file.write); any number of times; the value stays; - handing over: giving it to a word that takes it
owned(f file.close), storing it in a global or field, putting a row just taken into an index over its pool (insert, 8.10 (3a)), or returning it as anownedresult; after that the name is empty and cannot be read.
(3) An owned value is put into a local or handed over at once. It cannot be doubled with dup or discarded with drop. It shall be handed over on the paths of failures (or_fail or_return) and at return too.
12-owned-file.pnx
use io
use file
word write_note (path str or fail)
path file.open_write or_fail to f rem an owned file: it must be closed on every path
"a note" f file.write or_else (e) f file.close e fail end
f file.close
end
word read_note (path str gives s str or fail) path file.read_all or_fail end
"note.txt" write_note or_else (e) "cannot write" io.println end
"note.txt" read_note or_else (e) "cannot read" end io.println
Output
$ pnx 12-owned-file.pnx
a note
Constraints
(4) Using a value again after handing it over is refused.
12-owned-twice.pnx
use file
word f (path str or fail)
path file.open_read or_fail to h
h file.close
h file.close
end
Refused
$ pnx 12-owned-twice.pnx
12-owned-twice.pnx:5:3: this local gave its owned value away; it holds nothing to read
(5) Doubling or discarding an owned value is refused.
12-owned-dup.pnx
use file
word f (path str or fail)
path file.open_read or_fail dup file.close file.close
end
Refused
$ pnx 12-owned-dup.pnx
12-owned-dup.pnx:3:31: an owned value is kept in a local or given on at once; this word would lose it
(6) A single path that does not hand it over is enough to refuse.
12-owned-path.pnx
use file
word f (path str, quick bool or fail)
path file.open_read or_fail to h
quick if return end
h file.close
end
Refused
$ pnx 12-owned-path.pnx
12-owned-path.pnx:4:12: an owned value in a local is not given on every path (close, free or store it)
(7) In a run that ends with a fault, the processor closes the handles that run opened (11.3 (3)).
Diagnostics
| Condition | Message |
|---|---|
A value taken back is on the stack at release | a str made after this mark is still on the stack; the release gives its bytes back |
| Reading a value taken back | this str was made after a mark that was released since; the region may hold other bytes there now |
| Reading a value handed over | this local gave its owned value away; it holds nothing to read |
Doubling or discarding an owned value | an owned value is kept in a local or given on at once; this word would lose it |
| A path that does not hand it over | an owned value in a local is not given on every path (close, free or store it) |