[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