[m-dev.] thread safe mutable updates

Julien Fischer juliensf at csse.unimelb.edu.au
Mon Aug 14 18:28:35 AEST 2006


Peter Wang recently pointed at that the current "thread_safe" mutables
are really not; further more there is currently no way of atomically
updating the value of mutable based on its present value.

e.g. the following cannot be carried out as an atomic operation.

 	...
 	get_varname(X0, !IO),
 	X = Transform(X0),
 	set_varname(X, !IO),
 	...

The problem with the above is that it is possible for another thread to
change the value of varname while Transform is being executed.

I have just posted a diff that removes the existing (broken) thread_safe
mutable support.  Peter and I have discussed two proposals for extending
mutables in the presence of threads:

(1) add a `thread_local' attribute that allows for (optional) thread-local
     mutables.

(2) for mutables that are truly global (and hence potentially shared
     between threads) add a new mutable operation `update' that allows
     the value of the mutable to be atomically updated.

The following proposal addresses (2).

In the .par grades we will associate a mutex with each non-constant mutable.
The mutable transformation would become this (modulo some unimportant
details):

     :- mutable(varname, vartype, initial_value, varinst, [...]).

===>

     :- pragma foreign_decl("C", "

         extern <C type for vartype> varname;

 	#ifdef MR_THREAD_SAFE
 	    extern MercuryLock lock_for_varname;
 	#endif
      ").

      :- pragma foreign_code("C", "

 	<C type for vartype) varname;

 	#ifdef MR_THREAD_SAFE
 		extern MercuryLock lock_for_varname;
 	#endif
      ").

      :- impure pred initialise_mutable_varname(vartype::in) is det.

 	% XXX we would probably combine these two operations into a single
 	% foreign_proc.
      intialise_mutable_varname(X) :-
          impure init_lock_for_varname,
 	 impure set_varname(X).

      :- intialise initialise_mutable_varname/0.

      :- impure pred init_lock_for_varname is det
      :- pragma foreign_proc("C",
          init_lock_for_varname,
 	 [will_not_call_mercury],
      "
 	 #ifdef MR_THREAD_SAFE
             pthread_mutex_init(&lock_for_varname, MR_MUTEX_ATTR);
 	 #endif
      ").

      :- impure pred set_varname(vartype::in) is det.
      :- pragma foreign_proc("C",
          set_varname(X::in),
          [thread_safe, will_not_call_mercury],
      "
          #ifdef MR_THREAD_SAFE
          MR_LOCK(lock_for_varname, \"set_varname/1\");
          #endif

 	 varname = X;

 	 #ifdef MR_THREAD_SAFE
          MR_UNLOCK(lock_for_varname, \"set_varname/1\");
          #endif
      ").

 	:- semipure pred get_varname(vartype::out) is det.
 	:- pragma foreign_proc("C",
 	    get_varname(X::out),
 	    [promise_semipure, thread_safe, will_not_call_mercury],
 	"
 	    #ifdef MR_THREAD_SAFE
 	    MR_LOCK(lock_for_varname, \"get_varname/1\");
 	    #endif

 	    X = varname;

 	    #ifdef MR_THREAD_SAFE
 	    MR_UNLOCK(lock_for_varname, \"get_varname.1\");
 	    #endif
 	").

In addition to the get and set predicate we add a third operation, update
for non-constant mutables.  The update predicate has the following
signature:

 	:- impure pred update_varname((func(vartype) = vartype)).
 	:- mode   pred update_varname((func(in) = out is det)) is det.

Conceptually what it does is:

 	update_varname(F) :-
 		<acquire lock on varname>,
 		get_varname(V0),
 		V = F(V0),
 		set_varname(V),
 		<release lock on varname>.

In practice the implementation will be something like:

 	:- pragma foreign_proc("C",
 	    update_varname(Closure::((func(in) = out is det)),
 	    [will_not_call_mercury, thread_safe],
 	"
 	    #ifdef MR_THREAD_SAFE
 	    MR_LOCK(lock_for_varname, \"update_varname\");
 	    #endif

 	    varname = MR_call_update_varname_closure(Closure, varname);

 	    #ifdef MR_THREAD_SAFE
 	    MR_UNLOCK(lock_for_varname, \"update_varname\");
 	    #endif
 	").

 	:- pragma foreign_export("C",
 	   call_update_varname_closure((func(in) = out is det), in) = out,
 	   "MR_call_update_varname_closure").
 	:- func call_update_varname_closure((func(vartype) = vartype),
 	   vartype) = vartype.

 	call_update_varname_closure(Func, X) = Func(X).

In the presence of the `attach_to_io_state' attribute the compiler would
also create:

     :- pred update_varname((func(vartype) = vartype), io, io).
     :- mode update_varname((func(in) = out is det), di, uo) is det.

etc.

Julien.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at csse.unimelb.edu.au
Administrative Queries: owner-mercury-developers at csse.unimelb.edu.au
Subscriptions:          mercury-developers-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the developers mailing list