prolog: Memory overflows cannot be caught

What is really nice is that you have chosen current_prolog_flag(max_arity, unbounded)!

?- length(_,E), N is 2^E, writeq(N),nl, catch(functor(F,f,N), Error, true), nonv
ar(Error).
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
fatal error: runtime: out of memory

Expected: Error = error(resource_error(memory), _Imp_def).

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Comments: 17 (9 by maintainers)

Most upvoted comments

Here is a case where this limit is not respected:

ulrich@p0:/opt/gupu/ichiban-prolog$ go version
go version go1.20.4 linux/amd64
ulrich@p0:/opt/gupu/ichiban-prolog$ GOMEMLIMIT=1MiB $(go env GOPATH)/bin/1pl
Top level for ichiban/prolog v1.1.0
This is for testing purposes only!
See https://github.com/ichiban/prolog for more details.
Type Ctrl-C or 'halt.' to exit.
?- length(L,8).
L = [_65,_66,_67,_68,_69,_70,_71,_72];
?- length(L,16).
2023/06/05 12:17:38 error(resource_error(memory),length/2)  % that is perfect!
?- append(L,_,_),length(L,16).
L = [_85,_90,_95,_100,_105,_110,_115,_120,_125,_130,_135,_140,_145,_150,_155,_160].  % why now?
?- append(L,_,_),length(L,1024),!,fail.
false. % even larger
?- append(L,_,_),length(L,32768),!,fail.
   running. % still running for more than 15'
   false. % finally failed after 28'

Merged the fix and released as v0.15.1.

$ go install github.com/ichiban/prolog/cmd/1pl@latest
go: downloading github.com/ichiban/prolog v0.15.1
$ GOMEMLIMIT=500MiB $(go env GOPATH)/bin/1pl
Top level for ichiban/prolog v0.15.1
This is for testing purposes only!
See https://github.com/ichiban/prolog for more details.
Type Ctrl-C or 'halt.' to exit.
?- length(_,E), N is 2^E, writeq(N),nl, catch(functor(F,f,N), Error, true), nonv
ar(Error).
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
E = 24,
Error = error(resource_error(memory),functor/3),
F = _16780056,
N = 16777216.
?- length(_,E), N is 2^E, writeq(N),nl, catch(length(L,N), Error, true), nonvar(
Error).
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
E = 24,
Error = error(resource_error(memory),length/2),
L = _33559888,
N = 16777216.
?- 

You can, but Go runtime immediately exit(2) when it reached to the limit.

I’m preparing a fix to respect Go’s builtin soft memory limit in #279. You can set the limit with an environment variable GOMEMLIMIT. functor/3 and length/2 will respect the limit and when they are likely to breach the limit, they’ll throw error(resource_error(memory), _).

$ GOMEMLIMIT=500MiB $(go env GOPATH)/bin/1pl
Top level for ichiban/prolog (devel)
This is for testing purposes only!
See https://github.com/ichiban/prolog for more details.
Type Ctrl-C or 'halt.' to exit.
?- length(_,E), N is 2^E, writeq(N),nl, catch(functor(F,f,N), Error, true), nonv
ar(Error).
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
E = 24,
Error = error(resource_error(memory),functor/3),
F = _16780056,
N = 16777216

When combined with ulimit -v, Go’s official document recommends a 5-10% buffer.

In this case, a good rule of thumb is to leave an additional 5-10% of headroom to account for memory sources the Go runtime is unaware of. https://tip.golang.org/doc/gc-guide

Why don’t we let it crash, call it a limitation of this processor,

You can always resort to that at least for standard conformity. For that matter, you can even claim exit 1 to be a conforming system. See this for more. But by this you are reducing the domain of applicability of your system. Non-termination and thus such overflows are happening all the time to (at least) beginners. And a crashing system isn’t helpful in such situations.

and ask users to call functor/3 and length/2 with care?

Limiting max_arity means that you are putting the burden onto the programmer. So the programs have to be more complex than otherwise necessary. Instead of a simple arg/3 one has to program something more complex.

Well, it is up to you to decide which way you want to go.