%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 1998-2000,2002-2003, 2006, 2011 The University of Melbourne. % Copyright (C) 2018, 2022 The Mercury team. % This file is distributed under the terms specified in COPYING.LIB. %---------------------------------------------------------------------------% % % File: nb_reference.m. % Authors: pets (Peter Schachte). % Stability: low. % Purpose: A non-backtrackably modifiable storage type. % % This module provides impure code for manipulating a non-backtrackable % reference type. The basic idea is that you allocate a reference and pass it % through your code, and each time you dereference it you get the value most % recently assigned to it, even if the assignment has been backtracked over. % This gives you a way to communicate information between disjunctive % alternatives in your code. % % Because assignments to nb_references need to survive backtracking, every term % assigned to a nb_reference must be fully copied in some compiler grades. This % means that nb_references may be somewhat expensive to use for large terms. % However, dereferencing an nb_reference does not need to copy the term, so it % is very efficient. Furthermore, if (part of) a new value being assigned to a % nb_reference was itself (part of) an nb_reference, that part will not need to % be copied. For example, if you use nb_references to build up a long list of % terms one at a time, each time you add a new term to the list it will need to % be copied, but the old value will have already been copied so it will not % need to be copied again. % % One further issue arises due to the copying of terms. Because copied terms % are not reclaimed on failure, the only way they can be reclaimed is through % garbage collection. If you use nb_references in a grade without garbage % collection, they will never be reclaimed. % %---------------------------------------------------------------------------% :- module nb_reference. :- interface. % A non-backtrackably destructively modifiable reference type. % :- type nb_reference(T). % Create a new nb_reference given a term for it to reference. % :- impure pred new_nb_reference(T::in, nb_reference(T)::out) is det. % Get the value currently referred to by a nb_reference. % :- semipure pred value(nb_reference(T)::in, T::out) is det. % (Non-backtrackably) modify a nb_reference to refer to a new object. % :- impure pred update(nb_reference(T)::in, T::in) is det. % init(Ref, Value): % Initialise a reference Ref to have value Value. % This is for use with user-declared ME_NbReferences (see c_reference.h), % and must be called before using such a reference. Attempting to access % the reference before it is initialised is undefined. % :- impure pred init(nb_reference(T)::in, T::in) is det. % from_c_pointer(CPointer) = Ref: % Convert a c_pointer to a nb_reference. % :- func from_c_pointer(c_pointer) = nb_reference(T). % to_c_pointer(Ref) = CPointer: % Convert a nb_reference to a c_pointer. % :- func to_c_pointer(nb_reference(T)) = c_pointer. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. % This type is implemented in C. % Note that if the C type used to implement nb_references changes (from % something equivalent to `MR_Word'), then `c_reference.h' should also be % updated. :- type nb_reference(T) ---> nb_reference(private_builtin.ref(T)). :- pragma foreign_decl("C", "#include ""mercury_deep_copy.h"""). :- pragma inline(pred(new_nb_reference/2)). new_nb_reference(X, nb_reference(Ref)) :- impure new_nb_reference_2(X, Ref). :- impure pred new_nb_reference_2(T::in, private_builtin.ref(T)::out) is det. :- pragma inline(new_nb_reference_2/2). :- pragma foreign_proc("C", new_nb_reference_2(X::in, Ref::out), [will_not_call_mercury], " MR_incr_hp(Ref, 1); #ifndef MR_CONSERVATIVE_GC MR_save_transient_registers(); #endif *(MR_Word *) Ref = MR_make_long_lived(X, (MR_TypeInfo) TypeInfo_for_T, (MR_Word *) Ref); #ifndef MR_CONSERVATIVE_GC MR_restore_transient_registers(); #endif "). :- pragma inline(pred(value/2)). value(nb_reference(Ref), X) :- semipure value_2(Ref, X). :- semipure pred value_2(private_builtin.ref(T)::in, T::out) is det. :- pragma inline(value_2/2). :- pragma foreign_proc("C", value_2(Ref::in, X::out), [promise_semipure, will_not_call_mercury], " X = *(MR_Word *) Ref; "). :- pragma inline(pred(update/2)). update(nb_reference(Ref), X) :- impure update_2(Ref, X). :- impure pred update_2(private_builtin.ref(T)::in, T::in) is det. :- pragma inline(pred(update_2/2)). :- pragma foreign_proc("C", update_2(Ref::in, X::in), [will_not_call_mercury], " #ifndef MR_CONSERVATIVE_GC MR_save_transient_registers(); #endif *(MR_Word *) Ref = MR_make_long_lived(X, (MR_TypeInfo) TypeInfo_for_T, (MR_Word *) Ref); #ifndef MR_CONSERVATIVE_GC MR_restore_transient_registers(); #endif "). :- pragma inline(pred(init/2)). init(Ref, X) :- impure update(Ref, X). :- pragma inline(func(from_c_pointer/1)). from_c_pointer(CPointer) = nb_reference(Ref) :- private_builtin.unsafe_type_cast(CPointer, Ref). :- pragma inline(func(to_c_pointer/1)). to_c_pointer(nb_reference(Ref)) = CPointer :- private_builtin.unsafe_type_cast(Ref, CPointer). %---------------------------------------------------------------------------% :- end_module nb_reference. %---------------------------------------------------------------------------%