C BACKENDS For non-constant mutables the transformation is as follows: :- mutable(, , , , [attributes]). ===> :- pragma foreign_decl("C", " extern mutable_; #ifdef MR_THREAD_SAFE extern MercuryLock mutable__lock; #endif "). :- pragma foreign_code("C", " mutable_; #ifdef MR_THREAD_SAFE MercuryLock mutable__lock; #endif "). NOTES: * The name of the C global corresponding to mutable_ may be mangled. * is chosen on a backend-specific basis. If the value stored in the mutable is always boxed it is `MR_Word' otherwise it may be some native type, `MR_Integer', `MR_Float' etc. :- initialise initialise_mutable_/0. :- impure pred initialise_mutable_ is det. initialise_mutable_ :- impure pre_initialise_mutable_, impure X = , impure set_(X). :- impure pred pre_initialise_mutable_ is det. :- pragma foreign_proc("C", pre_initialise_mutable_, [will_not_call_mercury], " #ifdef MR_THREAD_SAFE pthread_init_mutex(&mutable__lock, MR_MUTEX_ATTR); #endif "). Operations on mutables are defined in terms of the following four predicates. Note that they are all marked `thread_safe' in order to avoid having to acquire the global lock. :- impure pred unsafe_set_(::in()) is det. :- pragma foreign_proc("C", unsafe_set_(X::in()), [will_not_call_mercury, thread_safe], " mutable_ = X; "). :- semipure pred unsafe_get_(::out()) is det. :- pragma foreign_proc("C", unsafe_get_(X::out()), [promise_semipure, will_not_call_mercury, thread_safe], " X = mutable_; "). :- impure lock_ is det. :- pragma foreign_proc("C", lock_, [will_not_call_mercury, promise_pure], " #ifdef MR_THREAD_SAFE MR_LOCK(&mutable__lock, \"lock_/0\"); #endif "). :- impure unlock_ is det. :- pragma foreign_proc("C", unlock_, [will_not_call_mercury, promise_pure], " #ifdef MR_THREAD_SAFE MR_UNLOCK(&mutable__lock, \"unlock_/0\"); #endif "). The other operations are all defined in Mercury using the above predicates: :- impure pred set_(::in()) is det. set_(X) :- impure lock_, impure unsafe_set_(X), impure unlock_. :- semipure pred get_(::out()) is det. get_(X) :- promise_semipure ( impure lock_ semipure unsafe_get_(X), impure unlock_ ). etc. For thread-local mutables the transformation is as above, with the following differences: :- mutable(, , , , [thread_local]). ===> :- pragma foreign_decl("C", "extern MR_Unsigned mutable_;"). :- pragma foreign_code("C", "MR_Unsigned mutable_;"). :- pragma foreign_proc("C", pre_initialise_mutable_, [will_not_call_mercury], " mutable_ = MR_new_thread_local_mutable_index(); "). :- pragma foreign_proc("C", unsafe_set_(X::in()), [will_not_call_mercury, thread_safe], " MR_set_thread_local_mutable(, X, mutable_); "). :- pragma foreign_proc("C", unsafe_get_(X::out()), [promise_semipure, will_not_call_mercury, thread_safe], " MR_get_thread_local_mutable(, X, mutable_); "). :- pragma foreign_proc("C", lock_, [will_not_call_mercury, promise_pure], " /* blank */ "). :- pragma foreign_proc("C", unlock_, [will_not_call_mercury, promise_pure], " /* blank */ "). For constant mutables the transformation is: :- mutable(, , , , [constant]). ===> :- pragma foreign_decl("C", "extern mutable_;"). :- pragma foreign_code("C", " mutable_;"). :- pred get_(::out()) is det. :- pragma foreign_proc("C", get_(X::out()), [will_not_call_mercury, promise_pure, thread_safe], " X = mutable_; "). In order to initialise constant mutables we generate the following: :- impure pred secret_initialization_only_set_( ::in()) is det. :- pragma foreign_proc("C", secret_initialization_only_set_(X::in()), [will_not_call_mercury], " mutable_ = X; "). :- initialise initialise_mutable_/0. :- impure pred initialise_mutable_ is det. initialise_mutable_ :- impure X = , impure secret_initialization_only_set_(X). %-----------------------------------------------------------------------------% JAVA BACKEND For non-constant mutables the transformation is as follows: :- mutable(, , , , [attributes]). ===> :- pragma foreign_code("Java", " static mutable_; "). :- initialise initialise_mutable_/0. :- impure pred initialise_mutable_ is det. initialise_mutable_ :- impure X = , impure set_(X). is either `int' or `java.lang.Object' (all other types). Operations on mutables are defined in terms of the following two predicates. They are actually "safe": by the Java specification, 32-bit variables are loaded/stored atomically. Doubles and longs may be treated as two 32-bit variables, but Mercury does not expose them yet. The predicates are named so to minimise the differences with the C backends. :- impure pred unsafe_set_(::in()) is det. :- pragma foreign_proc("Java", unsafe_set_(X::in()), [will_not_call_mercury, thread_safe], " mutable_ = X; "). :- semipure pred unsafe_get_(::out()) is det. :- pragma foreign_proc("Java", unsafe_get_(X::out()), [promise_semipure, will_not_call_mercury, thread_safe], " X = mutable_; "). If mutable_ has the type `java.lang.Object' a cast is required after the code above, to cast X to the correct type. This is handled by the MLDS code generator. For thread-local mutables the transformation is as follows: :- mutable(, , , , [attributes]). ===> :- pragma foreign_code("Java", " static java.lang.ThreadLocal mutable_ = new java.lang.InheritableThreadLocal(); "). :- pragma foreign_proc("Java", unsafe_set_(X::in()), [will_not_call_mercury, thread_safe], " mutable_.set(X); "). :- pragma foreign_proc("Java", unsafe_get_(X::out()), [promise_semipure, will_not_call_mercury, thread_safe], " X = mutable_.get(); "). is `java.lang.Integer' or `java.lang.Object'. The above predicates are called by these predicates, again to minimise differences with the C backends: :- impure pred set_(::in()) is det. set_(X) :- impure unsafe_set_(X). :- semipure pred get_(::out()) is det. get_(X) :- semipure unsafe_get_(X). For constant mutables the transformation is: :- mutable(, , , , [constant]). ===> :- pragma foreign_code("Java", " static mutable_; "). :- pred get_(::out()) is det. :- pragma foreign_proc("Java", get_(X::out()), [will_not_call_mercury, promise_pure, thread_safe], " X = mutable_; "). In order to initialise constant mutables we generate the following: :- impure pred secret_initialization_only_set_( ::in()) is det. :- pragma foreign_proc("Java", secret_initialization_only_set_(X::in()), [will_not_call_mercury], " mutable_ = X; "). :- initialise initialise_mutable_/0. :- impure pred initialise_mutable_ is det. initialise_mutable_ :- impure X = , impure secret_initialization_only_set_(X). %-----------------------------------------------------------------------------% C# BACKEND The C# implementation is analogous to the Java implementation, except for thread-local mutables, which are transformed as follows: :- mutable(, , , , [attributes]). ===> :- pragma foreign_code("C#", " private static int mutable_; "). :- initialise initialise_mutable_/0. :- impure pred initialise_mutable_ is det. initialise_mutable_ :- impure pre_initialise_mutable_, impure X = , impure set_(X). :- pragma foreign_proc("C#", pre_initialise_mutable_, [will_not_call_mercury], " mutable_ = runtime.ThreadLocalMutables.new_index(); "). :- pragma foreign_proc("C#", unsafe_set_(X::in()), [will_not_call_mercury, thread_safe], " runtime.ThreadLocalMutables.set(mutable_, X); "). :- pragma foreign_proc("C#", unsafe_get_(X::out()), [promise_semipure, will_not_call_mercury, thread_safe], " X = runtime.ThreadLocalMutables.get(mutable_); ").