[m-rev.] for review: Add thread.spawn_native/4 and thread.spawn/4.

Peter Wang novalazy at gmail.com
Fri Jun 27 14:25:55 AEST 2014


On Fri, 27 Jun 2014 11:54:00 +1000 (EST), Julien Fischer <jfischer at opturion.com> wrote:
> 
> Hi Peter,
> 
> On Wed, 25 Jun 2014, Peter Wang wrote:
> 
> > -%-----------------------------------------------------------------------------%
> > -
> > :- implementation.
> >
> > +:- import_module bool.
> > +:- import_module require.
> > +:- import_module string.
> > +
> > :- pragma foreign_decl("C", "
> > #ifndef MR_HIGHLEVEL_CODE
> >   #if (!defined(MR_EXEC_TRACE) && !defined(MR_DEEP_PROFILING)) || !defined(MR_USE_GCC_NONLOCAL_GOTOS)
> > @@ -91,32 +122,54 @@
> > #endif
> > ").
> >
> > +    % Nothing yet but it can be used to hold a thread id, joining, holding
> > +    % uncaught exception values, etc.
> > +:- type thread
> > +    --->    thread.
> 
> Using a dummy type as the definition for thread handles means that all
> thread handles will unify and compare as equal.  (Which surely shouldn't
> be the case if the originate from different calls to spawn.)

Here is a followup patch.

On second thought, I don't know if thread handles themselves should be
comparable.  I think we should provide a function to get the thread_id
which can be compared.

    :- func thread_id(thread) = thread_id.

Peter

---

Add thread ids to thread handles.

Thread handles should not be dummy types because all thread handles
would compare equal.  Add thread ids to thread handles.

library/thread.m:
    As above.
    
    	Reduce accessibility levels in C# and Java helper classes.

diff --git a/library/thread.m b/library/thread.m
index 428ee2a..4e3739b 100644
--- a/library/thread.m
+++ b/library/thread.m
@@ -122,10 +122,13 @@
 #endif
 ").
 
-    % Nothing yet but it can be used to hold a thread id, joining, holding
-    % uncaught exception values, etc.
+    % The thread id is not formally exposed yet but allows different thread
+    % handles to compare unequal.
+    %
 :- type thread
-    --->    thread.
+    --->    thread(thread_id).
+
+:- type thread_id == string.
 
 %-----------------------------------------------------------------------------%
 
@@ -200,41 +203,54 @@ spawn(Goal, Res, !IO) :-
 :- mode spawn_context(pred(in, di, uo) is cc_multi, out, di, uo) is cc_multi.
 
 spawn_context(Goal, Res, !IO) :-
-    spawn_context_2(Goal, Success, !IO),
+    spawn_context_2(Goal, Success, ThreadId, !IO),
     (
         Success = yes,
-        Res = ok(thread)
+        Res = ok(thread(ThreadId))
     ;
         Success = no,
         Res = error("Unable to spawn threads in this grade.")
     ).
 
-:- pred spawn_context_2(pred(thread, io, io), bool, io, io).
-:- mode spawn_context_2(pred(in, di, uo) is cc_multi, out, di, uo) is cc_multi.
+:- pred spawn_context_2(pred(thread, io, io), bool, string, io, io).
+:- mode spawn_context_2(pred(in, di, uo) is cc_multi, out, out, di, uo)
+    is cc_multi.
+
+spawn_context_2(_, no, "", !IO) :-
+    cc_multi_equal(!IO).
 
 :- pragma foreign_proc("C",
     spawn_context_2(Goal::(pred(in, di, uo) is cc_multi), Success::out,
-        _IO0::di, _IO::uo),
+        ThreadId::out, _IO0::di, _IO::uo),
     [promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
         may_not_duplicate],
 "
 #if !defined(MR_HIGHLEVEL_CODE)
 {
-    MR_Context  *ctxt;
+    MR_Context          *ctxt;
+    MR_ThreadLocalMuts  *tlm;
 
     ML_incr_thread_barrier_count();
 
     ctxt = MR_create_context(""spawn"", MR_CONTEXT_SIZE_REGULAR, NULL);
     ctxt->MR_ctxt_resume = MR_ENTRY(mercury__thread__spawn_begin_thread);
 
+    tlm = MR_clone_thread_local_mutables(MR_THREAD_LOCAL_MUTABLES);
+    ctxt->MR_ctxt_thread_local_mutables = tlm;
+
     /*
-    ** Store the closure on the top of the new context's stack.
+    ** Derive a thread id from the address of the thread-local mutable vector
+    ** for the Mercury thread. It should actually be more unique than a
+    ** context address as contexts are kept around and reused.
     */
+    ThreadId = MR_make_string(MR_ALLOC_ID, ""%p"", tlm);
+
+    /*
+    ** Store Goal and ThreadId on the top of the new context's stack.
+    */
+    ctxt->MR_ctxt_sp[0] = Goal;
+    ctxt->MR_ctxt_sp[-1] = (MR_Word) ThreadId;
 
-    *(ctxt->MR_ctxt_sp) = Goal;
-    ctxt->MR_ctxt_next = NULL;
-    ctxt->MR_ctxt_thread_local_mutables =
-        MR_clone_thread_local_mutables(MR_THREAD_LOCAL_MUTABLES);
     MR_schedule_context(ctxt);
 
     Success = MR_TRUE;
@@ -242,6 +258,7 @@ spawn_context(Goal, Res, !IO) :-
 #else /* MR_HIGHLEVEL_CODE */
 {
     Success = MR_FALSE;
+    ThreadId = MR_make_string_const("""");
 }
 #endif /* MR_HIGHLEVEL_CODE */
 ").
@@ -249,26 +266,28 @@ spawn_context(Goal, Res, !IO) :-
 %-----------------------------------------------------------------------------%
 
 spawn_native(Goal, Res, !IO) :-
-    spawn_native_2(Goal, Success, !IO),
+    spawn_native_2(Goal, Success, ThreadId, !IO),
     (
         Success = yes,
-        Res = ok(thread)
+        Res = ok(thread(ThreadId))
     ;
         Success = no,
         Res = error("Unable to create native thread.")
     ).
 
-:- pred spawn_native_2(pred(thread, io, io), bool, io, io).
-:- mode spawn_native_2(pred(in, di, uo) is cc_multi, out, di, uo) is cc_multi.
+:- pred spawn_native_2(pred(thread, io, io), bool, thread_id, io, io).
+:- mode spawn_native_2(pred(in, di, uo) is cc_multi, out, out, di, uo)
+    is cc_multi.
 
 :- pragma foreign_proc("C",
     spawn_native_2(Goal::(pred(in, di, uo) is cc_multi), Success::out,
-        _IO0::di, _IO::uo),
+        ThreadId::out, _IO0::di, _IO::uo),
     [promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
         may_not_duplicate],
 "
+    ThreadId = MR_make_string_const("""");
 #ifdef MR_THREAD_SAFE
-    Success = ML_create_exclusive_thread(Goal);
+    Success = ML_create_exclusive_thread(Goal, &ThreadId);
 #else
     Success = MR_FALSE;
 #endif
@@ -276,7 +295,7 @@ spawn_native(Goal, Res, !IO) :-
 
 :- pragma foreign_proc("C#",
     spawn_native_2(Goal::(pred(in, di, uo) is cc_multi), Success::out,
-        _IO0::di, _IO::uo),
+        ThreadId::out, _IO0::di, _IO::uo),
     [promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
         may_not_duplicate],
 "
@@ -284,22 +303,27 @@ spawn_native(Goal, Res, !IO) :-
         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));
+            new System.Threading.ThreadStart(mt.run));
+        ThreadId = thread.ManagedThreadId.ToString();
+        mt.setThreadId(ThreadId);
         thread.Start();
         Success = mr_bool.YES;
     } catch (System.Threading.ThreadStartException e) {
         Success = mr_bool.NO;
+        ThreadId = """";
     }
 ").
 
 :- pragma foreign_proc("Java",
     spawn_native_2(Goal::(pred(in, di, uo) is cc_multi), Success::out,
-        _IO0::di, _IO::uo),
+        ThreadId::out, _IO0::di, _IO::uo),
     [promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
         may_not_duplicate],
 "
-    MercuryThread mt = new MercuryThread((Object[]) Goal);
-    Thread thread = new Thread(mt);
+    final MercuryThread mt = new MercuryThread((Object[]) Goal);
+    final Thread thread = new Thread(mt);
+    ThreadId = String.valueOf(thread.getId());
+    mt.setThreadId(ThreadId);
     thread.start();
     Success = bool.YES;
 ").
@@ -384,7 +408,8 @@ INIT mercury_sys_init_thread_modules
     MR_define_entry(mercury__thread__spawn_begin_thread);
     {
         /* Call the closure placed the top of the stack. */
-        MR_r1 = *MR_sp;
+        MR_r1 = MR_stackvar(1); /* Goal */
+        MR_r2 = MR_stackvar(2); /* ThreadId */
         MR_noprof_call(MR_ENTRY(mercury__do_call_closure_1),
             MR_LABEL(mercury__thread__spawn_end_thread));
     }
@@ -444,7 +469,7 @@ INIT mercury_sys_init_thread_modules
 #if defined(MR_THREAD_SAFE)
   #include  <pthread.h>
 
-  MR_bool ML_create_exclusive_thread(MR_Word goal);
+  MR_bool ML_create_exclusive_thread(MR_Word goal, MR_String *thread_id);
   void *ML_exclusive_thread_wrapper(void *arg);
 
   typedef struct ML_ThreadWrapperArgs ML_ThreadWrapperArgs;
@@ -453,13 +478,14 @@ INIT mercury_sys_init_thread_modules
         MR_Word             goal;
         MR_ThreadLocalMuts  *thread_local_mutables;
         MR_bool             thread_started;
+        MR_String           thread_id;
   };
 #endif /* MR_THREAD_SAFE */
 ").
 
 :- pragma foreign_code("C", "
 #if defined(MR_THREAD_SAFE)
-  MR_bool ML_create_exclusive_thread(MR_Word goal)
+  MR_bool ML_create_exclusive_thread(MR_Word goal, MR_String *thread_id)
   {
     ML_ThreadWrapperArgs    args;
     pthread_t               thread;
@@ -473,6 +499,7 @@ INIT mercury_sys_init_thread_modules
     args.thread_local_mutables =
         MR_clone_thread_local_mutables(MR_THREAD_LOCAL_MUTABLES);
     args.thread_started = MR_FALSE;
+    args.thread_id = NULL;
 
     pthread_attr_init(&attrs);
     pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
@@ -486,6 +513,7 @@ INIT mercury_sys_init_thread_modules
     sem_destroy(&args.sem);
 
     if (args.thread_started) {
+        *thread_id = args.thread_id;
         return MR_TRUE;
     }
 
@@ -497,6 +525,7 @@ INIT mercury_sys_init_thread_modules
   {
     ML_ThreadWrapperArgs    *args = arg;
     MR_Word                 goal;
+    MR_String               thread_id;
 
     if (MR_init_thread(MR_use_now) == MR_FALSE) {
         args->thread_started = MR_FALSE;
@@ -514,14 +543,18 @@ INIT mercury_sys_init_thread_modules
     MR_assert(MR_THREAD_LOCAL_MUTABLES == NULL);
     MR_SET_THREAD_LOCAL_MUTABLES(args->thread_local_mutables);
 
+    thread_id = MR_make_string(MR_ALLOC_SITE_RUNTIME,
+        ""%"" MR_INTEGER_LENGTH_MODIFIER ""x"", MR_SELF_THREAD_ID);
+
     /*
     ** Take a copy of the goal before telling the parent we are ready.
     */
     goal = args->goal;
     args->thread_started = MR_TRUE;
+    args->thread_id = thread_id;
     MR_SEM_POST(&args->sem, ""ML_exclusive_thread_wrapper"");
 
-    ML_call_back_to_mercury_cc_multi(goal);
+    ML_call_back_to_mercury_cc_multi(goal, thread_id);
 
     MR_finalize_thread_engine();
 
@@ -532,20 +565,21 @@ INIT mercury_sys_init_thread_modules
 #endif /* MR_THREAD_SAFE */
 ").
 
-:- pred call_back_to_mercury(pred(thread, io, io), io, io).
-:- mode call_back_to_mercury(pred(in, di, uo) is cc_multi, di, uo) is cc_multi.
+:- pred call_back_to_mercury(pred(thread, io, io), thread_id, io, io).
+:- mode call_back_to_mercury(pred(in, di, uo) is cc_multi, in, di, uo)
+    is cc_multi.
 :- pragma foreign_export("C",
-    call_back_to_mercury(pred(in, di, uo) is cc_multi, di, uo),
+    call_back_to_mercury(pred(in, di, uo) is cc_multi, in, di, uo),
     "ML_call_back_to_mercury_cc_multi").
 :- pragma foreign_export("C#",
-    call_back_to_mercury(pred(in, di, uo) is cc_multi, di, uo),
+    call_back_to_mercury(pred(in, di, uo) is cc_multi, in, di, uo),
     "ML_call_back_to_mercury_cc_multi").
 :- pragma foreign_export("Java",
-    call_back_to_mercury(pred(in, di, uo) is cc_multi, di, uo),
+    call_back_to_mercury(pred(in, di, uo) is cc_multi, in, di, uo),
     "ML_call_back_to_mercury_cc_multi").
 
-call_back_to_mercury(Goal, !IO) :-
-    Goal(thread, !IO).
+call_back_to_mercury(Goal, ThreadId, !IO) :-
+    Goal(thread(ThreadId), !IO).
 
 %-----------------------------------------------------------------------------%
 
@@ -598,35 +632,47 @@ call_back_to_mercury(Goal, !IO) :-
 %-----------------------------------------------------------------------------%
 
 :- pragma foreign_code("C#", "
-public class MercuryThread {
-    object[] Goal;
-    object[] thread_local_mutables;
+private class MercuryThread {
+    private object[] Goal;
+    private object[] thread_local_mutables;
+    private string ThreadId;
 
-    public MercuryThread(object[] g, object[] tlmuts)
+    internal MercuryThread(object[] g, object[] tlmuts)
     {
         Goal = g;
         thread_local_mutables = tlmuts;
     }
 
-    public void execute_goal()
+    internal void setThreadId(string id)
+    {
+        ThreadId = id;
+    }
+
+    internal void run()
     {
         runtime.ThreadLocalMutables.set_array(thread_local_mutables);
-        thread.ML_call_back_to_mercury_cc_multi(Goal);
+        thread.ML_call_back_to_mercury_cc_multi(Goal, ThreadId);
     }
 }").
 
 :- pragma foreign_code("Java", "
-public static class MercuryThread implements Runnable {
-    final Object[] Goal;
+private static class MercuryThread implements Runnable {
+    private final Object[] Goal;
+    private String ThreadId;
 
-    public MercuryThread(Object[] g)
+    private MercuryThread(Object[] g)
     {
         Goal = g;
     }
 
+    private void setThreadId(String id)
+    {
+        ThreadId = id;
+    }
+
     public void run()
     {
-        thread.ML_call_back_to_mercury_cc_multi(Goal);
+        thread.ML_call_back_to_mercury_cc_multi(Goal, ThreadId);
     }
 }").



More information about the reviews mailing list