[m-rev.] diff: thread-local mutables for C#
Peter Wang
novalazy at gmail.com
Thu Oct 28 14:17:38 AEDT 2010
Branches: main
Implement thread-local mutables for C# backend correctly.
A child thread automatically inherits the values of thread-local
mutables from the thread that spawned it.
runtime/mercury_dotnet.cs.in:
Add a `ThreadLocalMutables' class that implements inheritable
thread-local mutables, using the same technique as for C grades.
library/thread.m:
Make newly spawned threads copy the values of thread-local mutables
from the parent thread.
compiler/make_hlds_passes.m:
Update the C# mutable transformation to use the methods in the
`ThreadLocalMutables' class.
compiler/prog_mutable.m:
Describe the C# thread-local mutable transformation.
diff --git a/compiler/make_hlds_passes.m b/compiler/make_hlds_passes.m
index 270b8ef..aed91fe 100644
--- a/compiler/make_hlds_passes.m
+++ b/compiler/make_hlds_passes.m
@@ -542,9 +542,12 @@ add_pass_1_mutable(Item, Status, !ModuleInfo, !Specs) :-
WantLockDecls = yes,
WantUnsafeAccessDecls = yes
;
- ( CompilationTarget = target_java
- ; CompilationTarget = target_csharp
- ),
+ CompilationTarget = target_csharp,
+ WantPreInitDecl = yes,
+ WantLockDecls = no,
+ WantUnsafeAccessDecls = yes
+ ;
+ CompilationTarget = target_java,
WantPreInitDecl = no,
WantLockDecls = no,
WantUnsafeAccessDecls = yes
@@ -2356,19 +2359,16 @@ get_csharp_java_mutable_global_foreign_defn(Lang, TargetMutableName, Type,
Lang = lang_csharp,
(
IsThreadLocal = mutable_not_thread_local,
- ThreadStaticAttribute = ""
+ ( Type = int_type ->
+ TypeStr = "int"
+ ;
+ TypeStr = "object"
+ )
;
IsThreadLocal = mutable_thread_local,
- % XXX C#: This does not inherit the value from the parent thread.
- % We will probably need to use the ThreadLocal<T> class instead.
- ThreadStaticAttribute = "[System.ThreadStatic] "
- ),
- ( Type = int_type ->
TypeStr = "int"
- ;
- TypeStr = "object"
),
- DefnBody = string.append_list([ThreadStaticAttribute,
+ DefnBody = string.append_list([
"static ", TypeStr, " ", TargetMutableName, ";\n"])
;
Lang = lang_java,
@@ -2407,7 +2407,7 @@ get_csharp_java_mutable_global_foreign_defn(Lang, TargetMutableName, Type,
add_csharp_java_mutable_preds(ItemMutable, Lang, TargetMutableName,
!Status, !ModuleInfo, !QualInfo, !Specs) :-
module_info_get_name(!.ModuleInfo, ModuleName),
- ItemMutable = item_mutable_info(MercuryMutableName, _Type, InitTerm, Inst,
+ ItemMutable = item_mutable_info(MercuryMutableName, Type, InitTerm, Inst,
MutAttrs, MutVarset, Context, _SeqNum),
IsConstant = mutable_var_constant(MutAttrs),
Attrs0 = default_attributes(Lang),
@@ -2436,29 +2436,71 @@ add_csharp_java_mutable_preds(ItemMutable, Lang, TargetMutableName,
InitSetPredName = mutable_set_pred_sym_name(ModuleName,
MercuryMutableName),
add_csharp_java_mutable_primitive_preds(Lang, TargetMutableName,
- ModuleName, MercuryMutableName, MutAttrs, Attrs, Inst, BoxPolicy,
- Context, !Status, !ModuleInfo, !QualInfo, !Specs),
+ ModuleName, MercuryMutableName, Type, MutAttrs, Attrs, Inst,
+ BoxPolicy, Context, !Status, !ModuleInfo, !QualInfo, !Specs),
add_ccsj_mutable_user_access_preds(ModuleName, MercuryMutableName,
MutAttrs, Lang,
Context, !Status, !ModuleInfo, !QualInfo, !Specs)
),
+ % The C# thread-local mutable implementation requires array indices to be
+ % allocated in pre-init predicates.
+ (
+ Lang = lang_csharp,
+ mutable_var_thread_local(MutAttrs) = mutable_thread_local
+ ->
+ add_csharp_thread_local_mutable_pre_init_pred(TargetMutableName,
+ ModuleName, MercuryMutableName, Attrs, CallPreInitExpr,
+ Context, !Status, !ModuleInfo, !QualInfo, !Specs)
+ ;
+ CallPreInitExpr = true_expr - Context
+ ),
add_csharp_java_mutable_initialisation(ModuleName, MercuryMutableName,
- MutVarset, InitSetPredName, InitTerm,
+ MutVarset, CallPreInitExpr, InitSetPredName, InitTerm,
Context, !Status, !ModuleInfo, !QualInfo, !Specs).
+:- pred add_csharp_thread_local_mutable_pre_init_pred(string::in,
+ module_name::in, string::in, pragma_foreign_proc_attributes::in, goal::out,
+ prog_context::in, import_status::in, import_status::out,
+ module_info::in, module_info::out, qual_info::in, qual_info::out,
+ list(error_spec)::in, list(error_spec)::out) is det.
+
+add_csharp_thread_local_mutable_pre_init_pred(TargetMutableName,
+ ModuleName, MutableName, Attrs, CallPreInitExpr,
+ Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
+ PreInitCode = string.append_list([
+ TargetMutableName, " = runtime.ThreadLocalMutables.new_index();\n"
+ ]),
+ PreInitPredName = mutable_pre_init_pred_sym_name(ModuleName,
+ MutableName),
+ PreInitForeignProc = pragma_foreign_proc(Attrs,
+ PreInitPredName,
+ pf_predicate,
+ [],
+ varset.init, % ProgVarSet
+ varset.init, % InstVarSet
+ fc_impl_ordinary(PreInitCode, yes(Context))
+ ),
+ PreInitItemPragma = item_pragma_info(compiler(mutable_decl),
+ PreInitForeignProc, Context, -1),
+ PreInitItem = item_pragma(PreInitItemPragma),
+ add_item_pass_3(PreInitItem, !Status, !ModuleInfo, !QualInfo,
+ !Specs),
+ CallPreInitExpr =
+ call_expr(PreInitPredName, [], purity_impure) - Context.
+
% Add the foreign clauses for the mutable's primitive access and
% locking predicates.
%
:- pred add_csharp_java_mutable_primitive_preds(
foreign_language::in(lang_csharp_java), string::in, module_name::in,
- string::in, mutable_var_attributes::in, pragma_foreign_proc_attributes::in,
- mer_inst::in, box_policy::in,
+ string::in, mer_type::in, mutable_var_attributes::in,
+ pragma_foreign_proc_attributes::in, mer_inst::in, box_policy::in,
prog_context::in, import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_csharp_java_mutable_primitive_preds(Lang, TargetMutableName, ModuleName,
- MutableName, MutAttrs, Attrs, Inst, BoxPolicy,
+ MutableName, Type, MutAttrs, Attrs, Inst, BoxPolicy,
Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
IsThreadLocal = mutable_var_thread_local(MutAttrs),
@@ -2468,16 +2510,25 @@ add_csharp_java_mutable_primitive_preds(Lang, TargetMutableName, ModuleName,
set_thread_safe(proc_thread_safe, GetAttrs0, GetAttrs),
varset.new_named_var(varset.init, "X", X, ProgVarSet),
(
- Lang = lang_csharp,
- GetCode = "\tX = " ++ TargetMutableName ++ ";\n"
- ;
- Lang = lang_java,
IsThreadLocal = mutable_not_thread_local,
GetCode = "\tX = " ++ TargetMutableName ++ ";\n"
;
+ IsThreadLocal = mutable_thread_local,
Lang = lang_java,
IsThreadLocal = mutable_thread_local,
GetCode = "\tX = " ++ TargetMutableName ++ ".get();\n"
+ ;
+ IsThreadLocal = mutable_thread_local,
+ Lang = lang_csharp,
+ ( Type = int_type ->
+ Cast = "(int) "
+ ;
+ Cast = ""
+ ),
+ GetCode = string.append_list([
+ "\tX = ", Cast, "runtime.ThreadLocalMutables.get(",
+ TargetMutableName, ");\n"
+ ])
),
GetForeignProc = pragma_foreign_proc(GetAttrs,
mutable_unsafe_get_pred_sym_name(ModuleName, MutableName),
@@ -2509,16 +2560,17 @@ add_csharp_java_mutable_primitive_preds(Lang, TargetMutableName, ModuleName,
TrailCode = ""
),
(
- Lang = lang_csharp,
- SetCode = "\t" ++ TargetMutableName ++ " = X;\n"
- ;
- Lang = lang_java,
IsThreadLocal = mutable_not_thread_local,
SetCode = "\t" ++ TargetMutableName ++ " = X;\n"
;
- Lang = lang_java,
IsThreadLocal = mutable_thread_local,
+ Lang = lang_java,
SetCode = "\t" ++ TargetMutableName ++ ".set(X);\n"
+ ;
+ IsThreadLocal = mutable_thread_local,
+ Lang = lang_csharp,
+ SetCode = "\truntime.ThreadLocalMutables.set(" ++
+ TargetMutableName ++ ", X);\n"
),
SetForeignProc = pragma_foreign_proc(SetAttrs,
mutable_unsafe_set_pred_sym_name(ModuleName, MutableName),
@@ -2536,13 +2588,13 @@ add_csharp_java_mutable_primitive_preds(Lang, TargetMutableName, ModuleName,
% Add the code required to initialise a mutable.
%
:- pred add_csharp_java_mutable_initialisation(module_name::in, string::in,
- prog_varset::in, sym_name::in, prog_term::in,
+ prog_varset::in, goal::in, sym_name::in, prog_term::in,
prog_context::in, import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_csharp_java_mutable_initialisation(ModuleName, MutableName, MutVarset,
- InitSetPredName, InitTerm,
+ CallPreInitExpr, InitSetPredName, InitTerm,
Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
% Add the `:- initialise' declaration for the mutable initialisation
% predicate.
@@ -2555,9 +2607,12 @@ add_csharp_java_mutable_initialisation(ModuleName, MutableName, MutVarset,
add_item_pass_3(InitItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Add the clause for the mutable initialisation predicate.
- InitClauseExpr =
+ CallSetPredExpr =
call_expr(InitSetPredName, [InitTerm], purity_impure)
- Context,
+ InitClauseExpr =
+ conj_expr(CallPreInitExpr, CallSetPredExpr)
+ - Context,
% See the comments for prog_io.parse_mutable_decl for the reason
% why we _must_ use MutVarset here.
diff --git a/compiler/prog_mutable.m b/compiler/prog_mutable.m
index 8ce4b2d..0481d4c 100644
--- a/compiler/prog_mutable.m
+++ b/compiler/prog_mutable.m
@@ -84,7 +84,7 @@
%
% :- semipure pred unsafe_get_<varname>(<vartype>::out(<varinst>)) is det.
% :- pragma foreign_proc("C",
-% unsafe_get_varname(X::out(<varinst>)),
+% unsafe_get_<varname>(X::out(<varinst>)),
% [promise_semipure, will_not_call_mercury, thread_safe],
% "
% X = mutable_<varname>;
@@ -148,14 +148,14 @@
% ").
%
% :- pragma foreign_proc("C",
-% unsafe_set_<varname)(X::in(<varinst>)),
+% unsafe_set_<varname>(X::in(<varinst>)),
% [will_not_call_mercury, thread_safe],
% "
% MR_set_thread_local_mutable(<type>, X, mutable_<varname>);
% ").
%
% :- pragma foreign_proc("C",
-% unsafe_get_varname(X::out(<varinst>)),
+% unsafe_get_<varname>(X::out(<varinst>)),
% [promise_semipure, will_not_call_mercury, thread_safe],
% "
% MR_get_thread_local_mutable(<type>, X, mutable_<varname>);
@@ -250,7 +250,7 @@
%
% :- semipure pred unsafe_get_<varname>(<vartype>::out(<varinst>)) is det.
% :- pragma foreign_proc("Java",
-% unsafe_get_varname(X::out(<varinst>)),
+% unsafe_get_<varname>(X::out(<varinst>)),
% [promise_semipure, will_not_call_mercury, thread_safe],
% "
% X = mutable_<varname>;
@@ -272,14 +272,14 @@
% ").
%
% :- pragma foreign_proc("Java",
-% unsafe_set_<varname)(X::in(<varinst>)),
+% unsafe_set_<varname>(X::in(<varinst>)),
% [will_not_call_mercury, thread_safe],
% "
% mutable_<varname>.set(X);
% ").
%
% :- pragma foreign_proc("Java",
-% unsafe_get_varname(X::out(<varinst>)),
+% unsafe_get_<varname>(X::out(<varinst>)),
% [promise_semipure, will_not_call_mercury, thread_safe],
% "
% X = mutable_<varname>.get();
@@ -339,6 +339,50 @@
%
%-----------------------------------------------------------------------------%
%
+% C# BACKEND
+%
+% The C# implementation is analogous to the Java implementation, except for
+% thread-local mutables, which are transformed as follows:
+%
+% :- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [attributes]).
+%
+% ===>
+%
+% :- pragma foreign_code("C#", "
+% private static int mutable_<varname>;
+% ").
+%
+% :- initialise initialise_mutable_<varname>/0.
+%
+% :- impure pred initialise_mutable_<varname> is det.
+%
+% initialise_mutable_<varname> :-
+% impure pre_initialise_mutable_<varname>,
+% impure set_<varname>(<initvalue>).
+%
+% :- pragma foreign_proc("C#",
+% pre_initialise_mutable_<varname>,
+% [will_not_call_mercury],
+% "
+% mutable_<varname> = runtime.ThreadLocalMutables.new_index();
+% ").
+%
+% :- pragma foreign_proc("C#",
+% unsafe_set_<varname>(X::in(<varinst>)),
+% [will_not_call_mercury, thread_safe],
+% "
+% runtime.ThreadLocalMutables.set(mutable_<varname>, X);
+% ").
+%
+% :- pragma foreign_proc("C#",
+% unsafe_get_<varname>(X::out(<varinst>)),
+% [promise_semipure, will_not_call_mercury, thread_safe],
+% "
+% X = runtime.ThreadLocalMutables.get(mutable_<varname>);
+% ").
+%
+%-----------------------------------------------------------------------------%
+%
% ERLANG BACKEND
%
% Every Erlang "process" has an associated process dictionary, which we can use
@@ -367,7 +411,7 @@
%
% :- impure pred set_<varname>(<vartype>::in(<varinst>)) is det.
% :- pragma foreign_proc("Erlang",
-% set_<varname)(X::in(<varinst>)),
+% set_<varname>(X::in(<varinst>)),
% [will_not_call_mercury, thread_safe],
% "
% 'ML_erlang_global_server' ! {set_mutable, <varname>, X}
@@ -375,7 +419,7 @@
%
% :- semipure pred get_<varname>(<vartype>::out(<varinst>)) is det.
% :- pragma foreign_proc("Erlang",
-% get_varname(X::out(<varinst>)),
+% get_<varname>(X::out(<varinst>)),
% [promise_semipure, will_not_call_mercury, thread_safe],
% "
% 'ML_erlang_global_server' ! {get_mutable, <varname>, self()}},
diff --git a/library/thread.m b/library/thread.m
index 589d113..d238764 100644
--- a/library/thread.m
+++ b/library/thread.m
@@ -153,12 +153,11 @@
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
may_not_duplicate],
"
- System.Threading.Thread t;
- MercuryThread mt = new MercuryThread(Goal);
-
- t = new System.Threading.Thread(
- new System.Threading.ThreadStart(mt.execute_goal));
- t.Start();
+ object[] thread_locals = runtime.ThreadLocalMutables.clone();
+ MercuryThread mt = new MercuryThread(Goal, thread_locals);
+ System.Threading.Thread thread = new System.Threading.Thread(
+ new System.Threading.ThreadStart(mt.execute_goal));
+ thread.Start();
").
:- pragma foreign_proc("Java",
@@ -421,14 +420,17 @@ call_back_to_mercury(Goal, !IO) :-
:- pragma foreign_code("C#", "
public class MercuryThread {
object[] Goal;
+ object[] thread_local_mutables;
- public MercuryThread(object[] g)
+ public MercuryThread(object[] g, object[] tlmuts)
{
Goal = g;
+ thread_local_mutables = tlmuts;
}
public void execute_goal()
{
+ runtime.ThreadLocalMutables.set_array(thread_local_mutables);
thread.ML_call_back_to_mercury_cc_multi(Goal);
}
}").
diff --git a/runtime/mercury_dotnet.cs.in b/runtime/mercury_dotnet.cs.in
index d1c29db..fdf9db7 100644
--- a/runtime/mercury_dotnet.cs.in
+++ b/runtime/mercury_dotnet.cs.in
@@ -938,6 +938,55 @@ public class UnreachableDefault : System.Exception {
/*---------------------------------------------------------------------------*/
+public class ThreadLocalMutables
+{
+ // Child threads do not inherit [ThreadStatic] values so we cannot use the
+ // attribute to implement `thread_local' mutables directly. Instead, all
+ // thread-local mutables are stored in a single array, which is copied from
+ // the parent thread when a new thread is spawned. The array indices are
+ // allocated by module initialisation predicates.
+ [System.ThreadStatic]
+ private static object[] array = null;
+
+ public static int new_index()
+ {
+ if (array == null) {
+ array = new object[1];
+ return 0;
+ } else {
+ int i = array.Length;
+ System.Array.Resize(ref array, i + 1);
+ return i;
+ }
+ }
+
+ public static void set(int i, object val)
+ {
+ array[i] = val;
+ }
+
+ public static object get(int i)
+ {
+ return array[i];
+ }
+
+ public static object[] clone()
+ {
+ if (array == null) {
+ return null;
+ } else {
+ return (object[]) array.Clone();
+ }
+ }
+
+ public static void set_array(object[] arr)
+ {
+ array = arr;
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+
public class Constants
{
public static readonly int MR_PREDICATE = 0;
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to: mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions: mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------
More information about the reviews
mailing list