mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-16 22:35:41 +00:00
Branches: main, 10.04
Allow inlining of Java foreign_procs.
This revealed a problem with directly using the `succeeded' flag directly as
the success indicator in Java foreign_procs. When the code of the foreign_proc
becomes a nested function, and after nested functions are eliminated, there may
not be a variable called `succeeded' in that context; it is moved into
environment struct, and the transformation is not able to update handwritten
code to reflect that. The solution is to declare a local variable for the
foreign_proc, let the handwritten code assign that, then assign its final
value to the `succeeded' flag with an MLDS statement.
We take the opportunity to name the local variable `SUCCESS_INDICATOR', in
line with other backends.
compiler/inlining.m:
Allow inlining of Java foreign_procs.
compiler/ml_foreign_proc_gen.m:
In the code generated for semidet Java foreign_procs, declare a local
`SUCCESS_INDICATOR' variable and assign its value to the `succeeded'
flag afterwards.
Add braces to give the foreign_proc variables a limited scope.
compiler/make_hlds_warn.m:
Conform to renaming.
doc/reference_manual.texi:
Update documentation for the renaming of the `succeeded' variable.
library/array.m:
library/bitmap.m:
library/builtin.m:
library/char.m:
library/construct.m:
library/dir.m:
library/exception.m:
library/float.m:
library/int.m:
library/io.m:
library/math.m:
library/private_builtin.m:
library/rtti_implementation.m:
library/string.m:
library/thread.m:
library/time.m:
library/type_desc.m:
library/version_array.m:
Conform to renaming.
Fix problems with Java foreign_procs that may now be copied into other
modules when intermodule optimisation is enabled, some by disallowing
the procedures from being duplicated, some by making referenced
classes/fields `public'.
[Some of the `may_not_duplicate' attributes may not indicate actual
problems, just that it seems not worthwhile inlining calls to the
procedure.]
extras/solver_types/library/any_array.m:
tests/hard_coded/equality_pred_which_requires_boxing.m:
tests/hard_coded/external_unification_pred.m:
tests/hard_coded/java_test.m:
tests/hard_coded/redoip_clobber.m:
tests/hard_coded/user_compare.m:
tests/valid/exported_foreign_type2.m:
tests/warnings/warn_succ_ind.m:
tests/warnings/warn_succ_ind.exp3:
Conform to renaming.
1204 lines
31 KiB
Mathematica
1204 lines
31 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ts=4 sw=4 et tw=0 wm=0 ft=mercury
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2004-2009 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
% vim: ft=mercury ts=4 sw=4 et wm=0 tw=0
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: version_array.m.
|
|
% Author: Ralph Becket <rafe@cs.mu.oz.au>.
|
|
% Stability: low.
|
|
%
|
|
% (See the header comments in version_types.m for an explanation of version
|
|
% types.)
|
|
%
|
|
% This module implements version arrays. A version array provides O(1)
|
|
% access and update for the "latest" version of the array. "Older"
|
|
% versions of the array incur an O(k) penalty on accesses where k is
|
|
% the number of updates that have been made since.
|
|
%
|
|
% The advantage of version arrays is that in the common, singly threaded,
|
|
% case, they are almost as fast as unique arrays, but can be treated as
|
|
% ordinary ground values rather than unique values.
|
|
%
|
|
% Version arrays are zero based.
|
|
%
|
|
% XXX This implementation is not yet guaranteed to work with the agc (accurate
|
|
% garbage collection) grades. Specifically, MR_deep_copy and MR_agc_deep_copy
|
|
% currently do not recognise version arrays.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module version_array.
|
|
:- interface.
|
|
|
|
:- import_module list.
|
|
|
|
:- type version_array(T).
|
|
|
|
% empty_array returns the empty array.
|
|
%
|
|
:- func empty = version_array(T).
|
|
|
|
% new(N, X) returns an array of size N with each item initialised to X.
|
|
%
|
|
:- func new(int, T) = version_array(T).
|
|
|
|
% A synonym for new/2.
|
|
%
|
|
:- func init(int, T) = version_array(T).
|
|
|
|
% Same as empty/0 except the resulting version_array is not thread safe.
|
|
%
|
|
% That is your program can segfault if you attempt to concurrently access
|
|
% or update the array from different threads, or any two arrays produced
|
|
% from operations on the same original array. However this version is much
|
|
% quicker if you guarantee that you never concurrently access the version
|
|
% array.
|
|
%
|
|
:- func unsafe_empty = version_array(T).
|
|
|
|
% Same as new(N, X) except the resulting version_array is not thread safe.
|
|
%
|
|
% That is your program can segfault if you attempt to concurrently access
|
|
% or update the array from different threads, or any two arrays produced
|
|
% from operations on the same original array. However this version is much
|
|
% quicker if you guarantee that you never concurrently access the version
|
|
% array.
|
|
%
|
|
:- func unsafe_new(int, T) = version_array(T).
|
|
|
|
% version_array(Xs) returns an array constructed from the items in the list
|
|
% Xs.
|
|
%
|
|
:- func version_array(list(T)) = version_array(T).
|
|
|
|
% A synonym for the above.
|
|
%
|
|
:- func from_list(list(T)) = version_array(T).
|
|
|
|
% A ^ elem(I) = X iff the Ith member of A is X (the first item has
|
|
% index 0).
|
|
%
|
|
:- func version_array(T) ^ elem(int) = T.
|
|
|
|
% lookup(A, I) = A ^ elem(I).
|
|
%
|
|
:- func lookup(version_array(T), int) = T.
|
|
|
|
% (A ^ elem(I) := X) is a copy of array A with item I updated to be
|
|
% X. An exception is thrown if I is out of bounds. set/4 is an
|
|
% equivalent predicate.
|
|
%
|
|
:- func (version_array(T) ^ elem(int) := T) = version_array(T).
|
|
|
|
:- pred set(int::in, T::in, version_array(T)::in, version_array(T)::out)
|
|
is det.
|
|
|
|
% size(A) = N if A contains N items (i.e. the valid indices for A
|
|
% range from 0 to N - 1).
|
|
%
|
|
:- func size(version_array(T)) = int.
|
|
|
|
% max(Z) = size(A) - 1.
|
|
%
|
|
:- func max(version_array(T)) = int.
|
|
|
|
% resize(A, N, X) returns a new array whose items from
|
|
% 0..min(size(A), N - 1) are taken from A and whose items
|
|
% from min(size(A), N - 1)..(N - 1) (if any) are initialised
|
|
% to X. A predicate version is also provided.
|
|
%
|
|
:- func resize(version_array(T), int, T) = version_array(T).
|
|
:- pred resize(int::in, T::in, version_array(T)::in, version_array(T)::out)
|
|
is det.
|
|
|
|
% list(A) = Xs where Xs is the list of items in A
|
|
% (i.e. A = version_array(Xs)).
|
|
%
|
|
:- func list(version_array(T)) = list(T).
|
|
|
|
% A synonym for the above.
|
|
%
|
|
:- func to_list(version_array(T)) = list(T).
|
|
|
|
% foldl(F, A, X) is equivalent to list.foldl(F, list(A), X).
|
|
%
|
|
:- func foldl(func(T1, T2) = T2, version_array(T1), T2) = T2.
|
|
|
|
% foldl(P, A, !X) is equivalent to list.foldl(P, list(A), !X).
|
|
%
|
|
:- pred foldl(pred(T1, T2, T2), version_array(T1), T2, T2).
|
|
:- mode foldl(pred(in, in, out) is det, in, in, out) is det.
|
|
:- mode foldl(pred(in, mdi, muo) is det, in, mdi, muo) is det.
|
|
:- mode foldl(pred(in, di, uo) is det, in, di, uo) is det.
|
|
|
|
% foldr(F, A, X) is equivalent to list.foldr(F, list(A), Xs).
|
|
%
|
|
:- func foldr(func(T1, T2) = T2, version_array(T1), T2) = T2.
|
|
|
|
% copy(A) is a copy of array A. Access to the copy is O(1).
|
|
%
|
|
:- func copy(version_array(T)) = version_array(T).
|
|
|
|
% unsafe_rewind(A) produces a version of A for which all accesses are
|
|
% O(1). Invoking this predicate renders A and all later versions undefined
|
|
% that were derived by performing individual updates. Only use this when
|
|
% you are absolutely certain there are no live references to A or later
|
|
% versions of A. (A predicate version is also provided.)
|
|
%
|
|
:- func unsafe_rewind(version_array(T)) = version_array(T).
|
|
:- pred unsafe_rewind(version_array(T)::in, version_array(T)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The first implementation of version arrays used nb_references.
|
|
% This incurred three memory allocations for every update. This version
|
|
% works at a lower level, but only performs one allocation per update.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module int.
|
|
:- import_module require.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
init(N, X) = version_array.new(N, X).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
version_array([]) = version_array.empty.
|
|
|
|
version_array([X | Xs]) =
|
|
version_array_2(1, Xs, version_array.new(1 + length(Xs), X)).
|
|
|
|
:- func version_array_2(int, list(T), version_array(T)) = version_array(T).
|
|
|
|
version_array_2(_, [], VA) = VA.
|
|
version_array_2(I, [X | Xs], VA) =
|
|
version_array_2(I + 1, Xs, VA ^ elem(I) := X).
|
|
|
|
from_list(Xs) = version_array(Xs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
VA ^ elem(I) =
|
|
( if get_if_in_range(VA, I, X)
|
|
then X
|
|
else func_error("version_array.elem: index out of range")
|
|
).
|
|
|
|
lookup(VA, I) = VA ^ elem(I).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
(VA0 ^ elem(I) := X) =
|
|
( if set_if_in_range(VA0, I, X, VA)
|
|
then VA
|
|
else func_error("version_array.'elem :=': index out of range")
|
|
).
|
|
|
|
set(I, X, VA, VA ^ elem(I) := X).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
max(VA) = size(VA) - 1.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
copy(VA) =
|
|
( if size(VA) = 0 then VA
|
|
else resize(VA, size(VA), VA ^ elem(0))
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
list(VA) = foldr(list.cons, VA, []).
|
|
|
|
to_list(VA) = list(VA).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
foldl(F, VA, Acc) = foldl_2(F, VA, Acc, 0, size(VA)).
|
|
|
|
:- func foldl_2(func(T1, T2) = T2, version_array(T1), T2, int, int) = T2.
|
|
|
|
foldl_2(F, VA, Acc, Lo, Hi) =
|
|
( if Lo < Hi then foldl_2(F, VA, F(VA ^ elem(Lo), Acc), Lo + 1, Hi)
|
|
else Acc
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
foldl(P, VA, !Acc) :-
|
|
foldl_2(P, VA, 0, size(VA), !Acc).
|
|
|
|
:- pred foldl_2(pred(T1, T2, T2), version_array(T1), int, int, T2, T2).
|
|
:- mode foldl_2(pred(in, in, out) is det, in, in, in, in, out) is det.
|
|
:- mode foldl_2(pred(in, mdi, muo) is det, in, in, in, mdi, muo) is det.
|
|
:- mode foldl_2(pred(in, di, uo) is det, in, in, in, di, uo) is det.
|
|
|
|
foldl_2(P, VA, Lo, Hi, !Acc) :-
|
|
( if Lo < Hi then
|
|
P(VA ^ elem(Lo), !Acc),
|
|
foldl_2(P, VA, Lo + 1, Hi, !Acc)
|
|
else
|
|
true
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
foldr(F, VA, Acc) = foldr_2(F, VA, Acc, size(VA) - 1).
|
|
|
|
:- func foldr_2(func(T1, T2) = T2, version_array(T1), T2, int) = T2.
|
|
|
|
foldr_2(F, VA, Acc, Hi) =
|
|
( if 0 =< Hi then foldr_2(F, VA, F(VA ^ elem(Hi), Acc), Hi - 1)
|
|
else Acc
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
unsafe_rewind(VA, unsafe_rewind(VA)).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
% Sordid stuff below this point...
|
|
%
|
|
% Note: this code is not thread safe, hence the absence of `thread_safe'
|
|
% attributes!
|
|
|
|
:- pragma foreign_type("C", version_array(T), "struct ML_va *")
|
|
where
|
|
equality is eq_version_array,
|
|
comparison is cmp_version_array.
|
|
|
|
:- pragma foreign_type("Java", version_array(T),
|
|
"jmercury.version_array.ML_va")
|
|
where
|
|
equality is eq_version_array,
|
|
comparison is cmp_version_array.
|
|
|
|
% This is necessary for the library to compile in the il and java
|
|
% grades.
|
|
:- type version_array(T)
|
|
---> version_array(T).
|
|
|
|
:- pragma terminates(eq_version_array/2).
|
|
:- pred eq_version_array(version_array(T)::in, version_array(T)::in)
|
|
is semidet.
|
|
|
|
eq_version_array(VAa, VAb) :-
|
|
N = max(VAa),
|
|
N = max(VAb),
|
|
eq_version_array_2(N, VAa, VAb).
|
|
|
|
:- pred eq_version_array_2(int::in,
|
|
version_array(T)::in, version_array(T)::in) is semidet.
|
|
|
|
eq_version_array_2(I, VAa, VAb) :-
|
|
( if I >= 0 then
|
|
VAa ^ elem(I) = VAb ^ elem(I),
|
|
eq_version_array_2(I - 1, VAa, VAb)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pragma terminates(cmp_version_array/3).
|
|
:- pred cmp_version_array(comparison_result::uo,
|
|
version_array(T)::in, version_array(T)::in) is det.
|
|
|
|
cmp_version_array(R, VAa, VAb) :-
|
|
SizeA = VAa ^ size,
|
|
SizeB = VAb ^ size,
|
|
compare(SizeResult, SizeA, SizeB),
|
|
(
|
|
SizeResult = (=),
|
|
cmp_version_array_2(0, SizeA, VAa, VAb, R)
|
|
;
|
|
( SizeResult = (<)
|
|
; SizeResult = (>)
|
|
),
|
|
R = SizeResult
|
|
).
|
|
|
|
:- pred cmp_version_array_2(int::in, int::in, version_array(T)::in,
|
|
version_array(T)::in, comparison_result::uo) is det.
|
|
|
|
cmp_version_array_2(I, Size, VAa, VAb, R) :-
|
|
( if I >= Size then
|
|
R = (=)
|
|
else
|
|
compare(R0, VAa ^ elem(I), VAb ^ elem(I)),
|
|
(
|
|
R0 = (=),
|
|
cmp_version_array_2(I + 1, Size, VAa, VAb, R)
|
|
;
|
|
( R0 = (<)
|
|
; R0 = (>)
|
|
),
|
|
R = R0
|
|
)
|
|
).
|
|
|
|
:- pragma foreign_proc("C",
|
|
version_array.empty = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
VA = MR_GC_NEW(struct ML_va);
|
|
|
|
VA->index = -1;
|
|
VA->value = (MR_Word) NULL;
|
|
VA->rest.array = (MR_ArrayPtr) MR_GC_NEW_ARRAY(MR_Word, 1);
|
|
VA->rest.array->size = 0;
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
VA->lock = MR_GC_NEW(MercuryLock);
|
|
pthread_mutex_init(VA->lock, MR_MUTEX_ATTR);
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
version_array.empty = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
VA = new ML_sva(ML_uva.empty());
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
version_array.unsafe_empty = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
VA = MR_GC_NEW(struct ML_va);
|
|
|
|
VA->index = -1;
|
|
VA->value = (MR_Word) NULL;
|
|
VA->rest.array = (MR_ArrayPtr) MR_GC_NEW_ARRAY(MR_Word, 1);
|
|
VA->rest.array->size = 0;
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
VA->lock = NULL;
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
version_array.unsafe_empty = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
VA = ML_uva.empty();
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
version_array.new(N::in, X::in) = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness, may_not_duplicate],
|
|
"
|
|
MR_Integer i;
|
|
|
|
VA = MR_GC_NEW(struct ML_va);
|
|
VA->index = -1;
|
|
VA->value = (MR_Word) NULL;
|
|
VA->rest.array = (MR_ArrayPtr) MR_GC_NEW_ARRAY(MR_Word, N + 1);
|
|
VA->rest.array->size = N;
|
|
|
|
for (i = 0; i < N; i++) {
|
|
VA->rest.array->elements[i] = X;
|
|
}
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
VA->lock = MR_GC_NEW(MercuryLock);
|
|
pthread_mutex_init(VA->lock, MR_MUTEX_ATTR);
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
version_array.new(N::in, X::in) = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness, may_not_duplicate],
|
|
"
|
|
VA = new ML_sva(ML_uva.init(N, X));
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
version_array.unsafe_new(N::in, X::in) = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness, may_not_duplicate],
|
|
"
|
|
MR_Integer i;
|
|
|
|
VA = MR_GC_NEW(struct ML_va);
|
|
VA->index = -1;
|
|
VA->value = (MR_Word) NULL;
|
|
VA->rest.array = (MR_ArrayPtr) MR_GC_NEW_ARRAY(MR_Word, N + 1);
|
|
VA->rest.array->size = N;
|
|
|
|
for (i = 0; i < N; i++) {
|
|
VA->rest.array->elements[i] = X;
|
|
}
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
VA->lock = NULL;
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
version_array.unsafe_new(N::in, X::in) = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness, may_not_duplicate],
|
|
"
|
|
VA = ML_uva.init(N, X);
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
resize(VA0::in, N::in, X::in) = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
VA = ML_va_resize_dolock(VA0, N, X);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
resize(VA0::in, N::in, X::in) = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness, may_not_duplicate],
|
|
"
|
|
VA = VA0.resize(N, X);
|
|
").
|
|
|
|
resize(N, X, VA, resize(VA, N, X)).
|
|
|
|
:- pragma foreign_proc("C",
|
|
size(VA::in) = (N::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
N = ML_va_size_dolock(VA);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
size(VA::in) = (N::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
N = VA.size();
|
|
").
|
|
|
|
:- pred get_if_in_range(version_array(T)::in, int::in, T::out) is semidet.
|
|
|
|
:- pragma foreign_proc("C",
|
|
get_if_in_range(VA::in, I::in, X::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
SUCCESS_INDICATOR = ML_va_get_dolock(VA, I, &X);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
get_if_in_range(VA::in, I::in, X::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
try {
|
|
X = VA.get(I);
|
|
SUCCESS_INDICATOR = true;
|
|
} catch (ArrayIndexOutOfBoundsException e) {
|
|
X = null;
|
|
SUCCESS_INDICATOR = false;
|
|
}
|
|
").
|
|
|
|
:- pred set_if_in_range(version_array(T)::in, int::in, T::in,
|
|
version_array(T)::out) is semidet.
|
|
|
|
:- pragma foreign_proc("C",
|
|
set_if_in_range(VA0::in, I::in, X::in, VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
SUCCESS_INDICATOR = ML_va_set_dolock(VA0, I, X, &VA);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
set_if_in_range(VA0::in, I::in, X::in, VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
try {
|
|
VA = VA0.set(I, X);
|
|
SUCCESS_INDICATOR = true;
|
|
} catch (ArrayIndexOutOfBoundsException e) {
|
|
VA = null;
|
|
SUCCESS_INDICATOR = false;
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
unsafe_rewind(VA0::in) = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
VA = ML_va_rewind_dolock(VA0);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
unsafe_rewind(VA0::in) = (VA::out),
|
|
[will_not_call_mercury, promise_pure, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
VA = VA0.rewind();
|
|
").
|
|
|
|
:- pragma foreign_decl("C", "
|
|
/*
|
|
** If index is -1 then value is undefined and rest is the latest
|
|
** array value.
|
|
**
|
|
** Otherwise value is the overwritten value at index and rest is
|
|
** a pointer to the next version in the chain.
|
|
*/
|
|
|
|
typedef struct ML_va *ML_va_ptr;
|
|
|
|
struct ML_va {
|
|
MR_Integer index; /* -1 for latest, >= 0 for older */
|
|
MR_Word value; /* Valid if index >= 0 */
|
|
union {
|
|
MR_ArrayPtr array; /* Valid if index == -1 */
|
|
ML_va_ptr next; /* Valid if index >= 0 */
|
|
} rest;
|
|
#ifdef MR_THREAD_SAFE
|
|
MercuryLock *lock; /* NULL or lock */
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
** Returns a pointer to the latest version of the array.
|
|
*/
|
|
extern ML_va_ptr ML_va_get_latest(ML_va_ptr VA);
|
|
|
|
/*
|
|
** Returns the number of items in a version array.
|
|
*/
|
|
extern MR_Integer ML_va_size_dolock(ML_va_ptr);
|
|
static MR_Integer ML_va_size(ML_va_ptr);
|
|
|
|
/*
|
|
** If I is in range then ML_va_get(VA, I, &X) sets X to the Ith item
|
|
** in VA (counting from zero) and returns MR_TRUE. Otherwise it
|
|
** returns MR_FALSE.
|
|
*/
|
|
extern int ML_va_get_dolock(ML_va_ptr, MR_Integer, MR_Word *);
|
|
static int ML_va_get(ML_va_ptr VA, MR_Integer I, MR_Word *Xptr);
|
|
|
|
/*
|
|
** If I is in range then ML_va_set(VA0, I, X, VA) sets VA to be VA0
|
|
** updated with the Ith item as X (counting from zero) and
|
|
** returns MR_TRUE. Otherwise it returns MR_FALSE.
|
|
*/
|
|
extern int ML_va_set_dolock(ML_va_ptr, MR_Integer, MR_Word,
|
|
ML_va_ptr *);
|
|
static int ML_va_set(ML_va_ptr, MR_Integer, MR_Word, ML_va_ptr *);
|
|
|
|
/*
|
|
** Create a copy of VA0 as a new array.
|
|
*/
|
|
static ML_va_ptr ML_va_flat_copy(const ML_va_ptr VA0);
|
|
|
|
/*
|
|
** Update the array VA using the override values in VA0
|
|
** i.e. recreate the state of the version array as captured in VA0.
|
|
*/
|
|
static void ML_va_rewind_into(ML_va_ptr VA, const ML_va_ptr VA0);
|
|
|
|
/*
|
|
** `Rewinds' a version array, invalidating all extant successors
|
|
** including the argument.
|
|
*/
|
|
extern ML_va_ptr ML_va_rewind_dolock(ML_va_ptr);
|
|
static ML_va_ptr ML_va_rewind(ML_va_ptr VA);
|
|
|
|
/*
|
|
** Resize a version array.
|
|
*/
|
|
extern ML_va_ptr ML_va_resize_dolock(ML_va_ptr, MR_Integer, MR_Word);
|
|
static ML_va_ptr ML_va_resize(ML_va_ptr, MR_Integer, MR_Word);
|
|
|
|
").
|
|
|
|
:- pragma foreign_code("C", "
|
|
|
|
#define ML_va_latest_version(VA) ((VA)->index == -1)
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
#define ML_maybe_lock(lock) \
|
|
do { \
|
|
if (lock) { \
|
|
MR_LOCK(lock, ""ML_maybe_lock""); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ML_maybe_unlock(lock) \
|
|
do { \
|
|
if (lock) { \
|
|
MR_UNLOCK(lock, ""ML_maybe_unlock""); \
|
|
} \
|
|
} while (0)
|
|
#else
|
|
#define ML_maybe_lock(lock) ((void) 0)
|
|
#define ML_maybe_unlock(lock) ((void) 0)
|
|
#endif
|
|
|
|
ML_va_ptr
|
|
ML_va_get_latest(ML_va_ptr VA)
|
|
{
|
|
while (!ML_va_latest_version(VA)) {
|
|
VA = VA->rest.next;
|
|
}
|
|
|
|
return VA;
|
|
}
|
|
|
|
MR_Integer
|
|
ML_va_size_dolock(ML_va_ptr VA)
|
|
{
|
|
#ifdef MR_THREAD_SAFE
|
|
MercuryLock *lock = VA->lock;
|
|
#endif
|
|
MR_Integer size;
|
|
|
|
ML_maybe_lock(lock);
|
|
|
|
size = ML_va_size(VA);
|
|
|
|
ML_maybe_unlock(lock);
|
|
|
|
return size;
|
|
}
|
|
|
|
static MR_Integer
|
|
ML_va_size(ML_va_ptr VA)
|
|
{
|
|
VA = ML_va_get_latest(VA);
|
|
|
|
return VA->rest.array->size;
|
|
}
|
|
|
|
int
|
|
ML_va_get_dolock(ML_va_ptr VA, MR_Integer I, MR_Word *Xptr)
|
|
{
|
|
#ifdef MR_THREAD_SAFE
|
|
MercuryLock *lock = VA->lock;
|
|
#endif
|
|
int ret;
|
|
|
|
ML_maybe_lock(lock);
|
|
|
|
ret = ML_va_get(VA, I, Xptr);
|
|
|
|
ML_maybe_unlock(lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ML_va_get(ML_va_ptr VA, MR_Integer I, MR_Word *Xptr)
|
|
{
|
|
while (!ML_va_latest_version(VA)) {
|
|
if (I == VA->index) {
|
|
*Xptr = VA->value;
|
|
return MR_TRUE;
|
|
}
|
|
|
|
VA = VA->rest.next;
|
|
}
|
|
|
|
if (0 <= I && I < VA->rest.array->size) {
|
|
*Xptr = VA->rest.array->elements[I];
|
|
return MR_TRUE;
|
|
} else {
|
|
return MR_FALSE;
|
|
}
|
|
}
|
|
|
|
int
|
|
ML_va_set_dolock(ML_va_ptr VA0, MR_Integer I, MR_Word X, ML_va_ptr *VAptr)
|
|
{
|
|
#ifdef MR_THREAD_SAFE
|
|
MercuryLock *lock = VA0->lock;
|
|
#endif
|
|
int ret;
|
|
|
|
ML_maybe_lock(lock);
|
|
|
|
ret = ML_va_set(VA0, I, X, VAptr);
|
|
|
|
ML_maybe_unlock(lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ML_va_set(ML_va_ptr VA0, MR_Integer I, MR_Word X, ML_va_ptr *VAptr)
|
|
{
|
|
ML_va_ptr VA1;
|
|
|
|
if (ML_va_latest_version(VA0)) {
|
|
if (I < 0 || I >= VA0->rest.array->size) {
|
|
return MR_FALSE;
|
|
}
|
|
|
|
VA1 = MR_GC_NEW(struct ML_va);
|
|
VA1->index = -1;
|
|
VA1->value = (MR_Word) NULL;
|
|
VA1->rest.array = VA0->rest.array;
|
|
#ifdef MR_THREAD_SAFE
|
|
VA1->lock = VA0->lock;
|
|
#endif
|
|
|
|
VA0->index = I;
|
|
VA0->value = VA0->rest.array->elements[I];
|
|
VA0->rest.next = VA1;
|
|
|
|
VA1->rest.array->elements[I] = X;
|
|
} else {
|
|
VA1 = ML_va_flat_copy(VA0);
|
|
|
|
if (I < 0 || I >= VA1->rest.array->size) {
|
|
return MR_FALSE;
|
|
}
|
|
|
|
VA1->rest.array->elements[I] = X;
|
|
}
|
|
|
|
*VAptr = VA1;
|
|
return MR_TRUE;
|
|
}
|
|
|
|
static ML_va_ptr
|
|
ML_va_flat_copy(const ML_va_ptr VA0)
|
|
{
|
|
ML_va_ptr latest;
|
|
ML_va_ptr VA;
|
|
MR_Integer N;
|
|
MR_Integer i;
|
|
|
|
latest = ML_va_get_latest(VA0);
|
|
N = latest->rest.array->size;
|
|
|
|
VA = MR_GC_NEW(struct ML_va);
|
|
VA->index = -1;
|
|
VA->value = (MR_Word) NULL;
|
|
VA->rest.array = (MR_ArrayPtr) MR_GC_NEW_ARRAY(MR_Word, N + 1);
|
|
VA->rest.array->size = N;
|
|
|
|
for (i = 0; i < N; i++) {
|
|
VA->rest.array->elements[i] = latest->rest.array->elements[i];
|
|
}
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
if (VA0->lock != NULL) {
|
|
VA->lock = MR_GC_NEW(MercuryLock);
|
|
pthread_mutex_init(VA->lock, MR_MUTEX_ATTR);
|
|
} else {
|
|
VA->lock = NULL;
|
|
}
|
|
#endif
|
|
|
|
ML_va_rewind_into(VA, VA0);
|
|
|
|
return VA;
|
|
}
|
|
|
|
static void
|
|
ML_va_rewind_into(ML_va_ptr VA, const ML_va_ptr VA0)
|
|
{
|
|
MR_Integer I;
|
|
MR_Word X;
|
|
|
|
if (ML_va_latest_version(VA0)) {
|
|
return;
|
|
}
|
|
|
|
ML_va_rewind_into(VA, VA0->rest.next);
|
|
|
|
I = VA0->index;
|
|
X = VA0->value;
|
|
if (I < VA->rest.array->size) {
|
|
VA->rest.array->elements[I] = X;
|
|
}
|
|
}
|
|
|
|
ML_va_ptr
|
|
ML_va_rewind_dolock(ML_va_ptr VA)
|
|
{
|
|
#ifdef MR_THREAD_SAFE
|
|
MercuryLock *lock = VA->lock;
|
|
#endif
|
|
ML_maybe_lock(lock);
|
|
|
|
VA = ML_va_rewind(VA);
|
|
|
|
ML_maybe_unlock(lock);
|
|
|
|
return VA;
|
|
}
|
|
|
|
static ML_va_ptr
|
|
ML_va_rewind(ML_va_ptr VA)
|
|
{
|
|
MR_Integer I;
|
|
MR_Word X;
|
|
|
|
if (ML_va_latest_version(VA)) {
|
|
return VA;
|
|
}
|
|
|
|
I = VA->index;
|
|
X = VA->value;
|
|
VA = ML_va_rewind(VA->rest.next);
|
|
VA->rest.array->elements[I] = X;
|
|
|
|
return VA;
|
|
}
|
|
|
|
ML_va_ptr
|
|
ML_va_resize_dolock(ML_va_ptr VA0, MR_Integer N, MR_Word X)
|
|
{
|
|
#ifdef MR_THREAD_SAFE
|
|
MercuryLock *lock = VA0->lock;
|
|
#endif
|
|
ML_va_ptr VA;
|
|
|
|
ML_maybe_lock(lock);
|
|
|
|
VA = ML_va_resize(VA0, N, X);
|
|
|
|
ML_maybe_unlock(lock);
|
|
|
|
return VA;
|
|
}
|
|
|
|
static ML_va_ptr
|
|
ML_va_resize(ML_va_ptr VA0, MR_Integer N, MR_Word X)
|
|
{
|
|
ML_va_ptr latest;
|
|
ML_va_ptr VA;
|
|
MR_Integer i;
|
|
MR_Integer size_VA0;
|
|
MR_Integer min;
|
|
|
|
latest = ML_va_get_latest(VA0);
|
|
|
|
size_VA0 = ML_va_size(latest);
|
|
min = (N <= size_VA0 ? N : size_VA0);
|
|
VA = MR_GC_NEW(struct ML_va);
|
|
|
|
VA->index = -1;
|
|
VA->value = (MR_Word) NULL;
|
|
VA->rest.array = (MR_ArrayPtr) MR_GC_NEW_ARRAY(MR_Word, N + 1);
|
|
VA->rest.array->size = N;
|
|
|
|
for (i = 0; i < min; i++) {
|
|
VA->rest.array->elements[i] = latest->rest.array->elements[i];
|
|
}
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
if (VA0->lock != NULL) {
|
|
VA->lock = MR_GC_NEW(MercuryLock);
|
|
pthread_mutex_init(VA->lock, MR_MUTEX_ATTR);
|
|
} else {
|
|
VA->lock = NULL;
|
|
}
|
|
#endif
|
|
|
|
ML_va_rewind_into(VA, VA0);
|
|
|
|
for (i = min; i < N; i++) {
|
|
VA->rest.array->elements[i] = X;
|
|
}
|
|
|
|
return VA;
|
|
}
|
|
|
|
").
|
|
|
|
:- pragma foreign_code("Java", "
|
|
|
|
public interface ML_va {
|
|
public Object get(int I) throws ArrayIndexOutOfBoundsException;
|
|
public ML_va set(int I, Object X);
|
|
public ML_va resize(int N, Object X);
|
|
public ML_va rewind();
|
|
public int size();
|
|
}
|
|
|
|
// An implementation of version arrays that is safe when used in multiple
|
|
// threads.
|
|
//
|
|
// It just wraps the unsafe version is some synchronization logic so
|
|
// that only one thread can be accessing the array at one instant.
|
|
public static class ML_sva implements ML_va {
|
|
private ML_uva version_array;
|
|
private Object lock;
|
|
|
|
public ML_sva(ML_uva va) {
|
|
version_array = va;
|
|
lock = new Object();
|
|
}
|
|
|
|
private ML_sva() {};
|
|
|
|
public Object get(int I) throws ArrayIndexOutOfBoundsException {
|
|
synchronized (lock) {
|
|
return version_array.get(I);
|
|
}
|
|
}
|
|
|
|
public ML_sva set(int I, Object X) {
|
|
synchronized (lock) {
|
|
ML_sva result = new ML_sva();
|
|
|
|
result.version_array = version_array.set(I, X);
|
|
|
|
if (result.version_array.isClone()) {
|
|
result.version_array.resetIsClone();
|
|
result.lock = new Object();
|
|
} else {
|
|
result.lock = this.lock;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public ML_sva resize(int N, Object X) {
|
|
synchronized (lock) {
|
|
ML_sva result = new ML_sva();
|
|
result.version_array = version_array.resize(N, X);
|
|
result.lock = new Object();
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public ML_sva rewind()
|
|
{
|
|
synchronized (lock) {
|
|
ML_sva result = new ML_sva();
|
|
result.version_array = version_array.rewind();
|
|
result.lock = this.lock;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public int size()
|
|
{
|
|
synchronized (lock) {
|
|
return version_array.size();
|
|
}
|
|
}
|
|
}
|
|
|
|
// An implementation of version arrays that is only safe when used from
|
|
// a single thread, but *much* faster than the synchronized version.
|
|
public static class ML_uva implements ML_va {
|
|
private int index; /* -1 for latest, >= 0 for older */
|
|
private Object value; /* Valid if index >= 0 */
|
|
private Object rest; /* array if index == -1 */
|
|
/* next if index >= 0 */
|
|
|
|
private boolean clone = false;
|
|
|
|
public ML_uva() {}
|
|
|
|
public static ML_uva empty() {
|
|
ML_uva va = new ML_uva();
|
|
va.index = -1;
|
|
va.value = null;
|
|
va.rest = new Object[0];
|
|
return va;
|
|
}
|
|
|
|
public static ML_uva init(int N, Object X) {
|
|
ML_uva va = new ML_uva();
|
|
va.index = -1;
|
|
va.value = null;
|
|
va.rest = new Object[N];
|
|
java.util.Arrays.fill(va.array(), X);
|
|
return va;
|
|
}
|
|
|
|
public ML_uva resize(int N, Object X) {
|
|
ML_uva VA0 = this;
|
|
ML_uva latest;
|
|
int size_VA0;
|
|
int min;
|
|
|
|
latest = VA0.latest();
|
|
|
|
size_VA0 = latest.size();
|
|
min = (N <= size_VA0 ? N : size_VA0);
|
|
ML_uva VA = new ML_uva();
|
|
|
|
VA.index = -1;
|
|
VA.value = null;
|
|
VA.rest = new Object[N];
|
|
|
|
System.arraycopy(latest.array(), 0, VA.array(), 0, min);
|
|
|
|
VA0.rewind_into(VA);
|
|
|
|
java.util.Arrays.fill(VA.array(), min, N, X);
|
|
return VA;
|
|
}
|
|
|
|
private boolean is_latest()
|
|
{
|
|
return index == -1;
|
|
}
|
|
|
|
private ML_uva latest()
|
|
{
|
|
ML_uva VA = this;
|
|
while (!VA.is_latest()) {
|
|
VA = VA.next();
|
|
}
|
|
return VA;
|
|
}
|
|
|
|
private Object[] array()
|
|
{
|
|
return (Object[]) rest;
|
|
}
|
|
|
|
private ML_uva next()
|
|
{
|
|
return (ML_uva) rest;
|
|
}
|
|
|
|
public int size()
|
|
{
|
|
return latest().array().length;
|
|
}
|
|
|
|
public Object get(int I)
|
|
throws ArrayIndexOutOfBoundsException
|
|
{
|
|
ML_uva VA = this;
|
|
|
|
while (!VA.is_latest()) {
|
|
if (I == VA.index) {
|
|
return VA.value;
|
|
}
|
|
|
|
VA = VA.next();
|
|
}
|
|
|
|
return VA.array()[I];
|
|
}
|
|
|
|
public ML_uva set(int I, Object X)
|
|
{
|
|
ML_uva VA0 = this;
|
|
ML_uva VA1;
|
|
|
|
if (VA0.is_latest()) {
|
|
VA1 = new ML_uva();
|
|
VA1.index = -1;
|
|
VA1.value = null;
|
|
VA1.rest = VA0.array();
|
|
|
|
VA0.index = I;
|
|
VA0.value = VA0.array()[I];
|
|
VA0.rest = VA1;
|
|
|
|
VA1.array()[I] = X;
|
|
} else {
|
|
VA1 = VA0.flat_copy();
|
|
|
|
VA1.array()[I] = X;
|
|
}
|
|
|
|
return VA1;
|
|
}
|
|
|
|
private ML_uva flat_copy()
|
|
{
|
|
ML_uva VA0 = this;
|
|
ML_uva latest;
|
|
ML_uva VA;
|
|
int N;
|
|
|
|
latest = VA0.latest();
|
|
N = latest.size();
|
|
|
|
VA = new ML_uva();
|
|
VA.index = -1;
|
|
VA.value = null;
|
|
VA.rest = latest.array().clone();
|
|
VA.clone = true;
|
|
|
|
VA0.rewind_into(VA);
|
|
|
|
return VA;
|
|
}
|
|
|
|
public boolean isClone() {
|
|
return clone;
|
|
}
|
|
|
|
public void resetIsClone() {
|
|
this.clone = false;
|
|
}
|
|
|
|
private void rewind_into(ML_uva VA)
|
|
{
|
|
int I;
|
|
Object X;
|
|
|
|
if (this.is_latest()) {
|
|
return;
|
|
}
|
|
|
|
this.next().rewind_into(VA);
|
|
|
|
I = this.index;
|
|
X = this.value;
|
|
if (I < VA.size()) {
|
|
VA.array()[I] = X;
|
|
}
|
|
}
|
|
|
|
public ML_uva rewind()
|
|
{
|
|
ML_uva VA = this;
|
|
int I;
|
|
Object X;
|
|
|
|
if (VA.is_latest()) {
|
|
return VA;
|
|
}
|
|
|
|
I = VA.index;
|
|
X = VA.value;
|
|
VA = VA.next().rewind();
|
|
VA.array()[I] = X;
|
|
|
|
return VA;
|
|
}
|
|
}
|
|
|
|
").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|