[m-rev.] for review: make updates to the I/O globals thread safe

Julien Fischer juliensf at csse.unimelb.edu.au
Wed Aug 16 17:33:58 AEST 2006


(This is still pending a bootcheck.)

For review by Peter Wang.

Estimated hours taken: 2
Branches: main

Make accessing the globals field in the I/O state properly thread safe by
applying a variation of the transformation recently proposed for mutables on
mercury-developers.

Add a predicate that allows the globals field to be updated atomically in the
presence of multiple threads.

library/io.m:
 	Attach a mutex to ML_io_user_globals and use that govern access to the
 	globals field in the I/O state.

 	Add a predicate the allows the globals field to be updated atomically.

NEWS:
 	Mention the new addition.

Julien.

Index: NEWS
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/NEWS,v
retrieving revision 1.418
diff -u -r1.418 NEWS
--- NEWS	8 Aug 2006 05:26:38 -0000	1.418
+++ NEWS	16 Aug 2006 07:27:15 -0000
@@ -14,6 +14,10 @@
  * We have added list.map2_foldl3/10, list.map_corresponding_foldl/6 and
    list.map_corresponding3_foldl/7

+* We have added io.update_globals/3 which allows for atomic updates to
+  the globals field in the I/O state in the presence of multiple threads.
+
+
  DETAILED LISTING
  ================

Index: library/io.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/io.m,v
retrieving revision 1.351
diff -u -r1.351 io.m
--- library/io.m	4 Aug 2006 07:36:21 -0000	1.351
+++ library/io.m	16 Aug 2006 07:21:52 -0000
@@ -1087,7 +1087,7 @@
  :- pred io.get_exit_status(int::out, io::di, io::uo) is det.
  :- pred io.set_exit_status(int::in, io::di, io::uo) is det.

-    % The io.state includes a `globals' field which is not used by the I/O
+    % The I/O state includes a `globals' field which is not used by the I/O
      % library, but can be used by the application. The globals field is
      % of type `univ' so that the application can store any data it wants there.
      % The following predicates can be used to access this global state.
@@ -1095,9 +1095,21 @@
      % Does not modify the I/O state.
      %
  :- pred io.get_globals(univ::uo, io::di, io::uo) is det.
-
  :- pred io.set_globals(univ::di, io::di, io::uo) is det.

+    % io.update_globals(UpdatePred, !IO).
+    % Update the `globals' field in the I/O state based upon its current
+    % value.  This is equivalent to the following:
+    %
+    %   io.get_globals(Globals0, !IO),
+    %   UpdatePred(Globals0, Globals),
+    %   io.set_globals(Globals, !IO)
+    %
+    % In parallel grades calls to io.update_globals/3 are atomic.
+    % 
+:- pred io.update_globals(pred(univ, univ)::in(pred(di, uo) is det),
+    io::di, io::uo) is det.
+
      % The following predicates provide an interface to the environment list.
      % Do not attempt to put spaces or '=' signs in the names of environment
      % variables, or bad things may result!
@@ -1530,6 +1542,11 @@
  :- pragma foreign_decl("C", "
      extern MR_Word      ML_io_stream_db;
      extern MR_Word      ML_io_user_globals;
+
+    #ifdef MR_THREAD_SAFE
+        extern MercuryLock ML_io_user_globals_lock;
+    #endif
+
      extern int          ML_next_stream_id;
      #if 0
        extern MR_Word    ML_io_ops_table;
@@ -1539,6 +1556,11 @@
  :- pragma foreign_code("C", "
      MR_Word         ML_io_stream_db;
      MR_Word         ML_io_user_globals;
+ 
+    #ifdef MR_THREAD_SAFE
+        MercuryLock ML_io_user_globals_lock;
+    #endif
+
      /* a counter used to generate unique stream ids */
      int             ML_next_stream_id;
      #if 0
@@ -4552,57 +4574,115 @@

  %-----------------------------------------------------------------------------%
  %-----------------------------------------------------------------------------%
-
-% Global state predicates.
-
+%
+% Global state predicates
+%
      % XXX design flaw with regard to unique modes
      % and io.get_globals/3: the `Globals::uo' mode here is a lie.

+io.get_globals(Globals, !IO) :-
+    io.lock_globals(!IO),
+    io.unsafe_get_globals(Globals, !IO),
+    io.unlock_globals(!IO).
+
+io.set_globals(Globals, !IO) :-
+    io.lock_globals(!IO),
+    io.unsafe_set_globals(Globals, !IO),
+    io.unlock_globals(!IO).
+
+io.update_globals(UpdatePred, !IO) :-
+    io.lock_globals(!IO),
+    io.unsafe_get_globals(Globals0, !IO),
+    UpdatePred(Globals0, Globals),
+    io.unsafe_set_globals(Globals, !IO),
+    io.unlock_globals(!IO).
+
+:- pred io.lock_globals(io::di, io::uo) is det.
  :- pragma foreign_proc("C",
-    io.get_globals(Globals::uo, IOState0::di, IOState::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    io.lock_globals(IO0::di, IO::uo),
+    [promise_pure, will_not_call_mercury, thread_safe, tabled_for_io],
+"
+    #ifdef MR_THREAD_SAFE
+        MR_LOCK(&ML_io_user_globals_lock, \"io.lock_globals/2\");
+    #endif
+ 
+    MR_update_io(IO0, IO);
+").
+
+    % For the non-C backends.
+    %
+lock_globals(!IO).
+
+:- pred io.unlock_globals(io::di, io::uo) is det.
+:- pragma foreign_proc("C",
+    io.unlock_globals(IO0::di, IO::uo),
+    [promise_pure, will_not_call_mercury, thread_safe, tabled_for_io],
+"
+    #ifdef MR_THREAD_SAFE
+        MR_UNLOCK(&ML_io_user_globals_lock, \"io.unlock_globals/2\");
+    #endif
+
+    MR_update_io(IO0, IO);
+").
+
+    % For the non-C backends.
+    %
+unlock_globals(!IO).
+
+    % NOTE: io.unsafe_{get, set}_globals/3 are marked as `thread_safe' so that
+    % calling them to does not acquire the global lock.  Since calls to these
+    % predicates should be surrounded by calls to io.{lock, unlock}_globals/2
+    % this is safe.
+    % 
+:- pred io.unsafe_get_globals(univ::uo, io::di, io::uo) is det.
+:- pragma foreign_proc("C",
+    io.unsafe_get_globals(Globals::uo, IO0::di, IO::uo),
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
  "
      Globals = ML_io_user_globals;
-    MR_update_io(IOState0, IOState);
+    MR_update_io(IO0, IO);
  ").

+:- pred io.unsafe_set_globals(univ::di, io::di, io::uo) is det.
  :- pragma foreign_proc("C",
-    io.set_globals(Globals::di, IOState0::di, IOState::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    io.unsafe_set_globals(Globals::di, IO0::di, IO::uo),
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
  "
      /* XXX need to globalize the memory */
      ML_io_user_globals = Globals;
-    MR_update_io(IOState0, IOState);
+    MR_update_io(IO0, IO);
  ").

  :- pragma foreign_proc("C#",
-    io.get_globals(Globals::uo, _IOState0::di, _IOState::uo),
+    io.unsafe_get_globals(Globals::uo, _IOState0::di, _IOState::uo),
      [will_not_call_mercury, promise_pure, tabled_for_io],
  "
      Globals = ML_io_user_globals;
  ").

  :- pragma foreign_proc("C#",
-    io.set_globals(Globals::di, _IOState0::di, _IOState::uo),
+    io.unsafe_set_globals(Globals::di, _IOState0::di, _IOState::uo),
      [will_not_call_mercury, promise_pure, tabled_for_io],
  "
      ML_io_user_globals = Globals;
  ").

  :- pragma foreign_proc("Java",
-    io.get_globals(Globals::uo, _IOState0::di, _IOState::uo),
+    io.unsafe_get_globals(Globals::uo, _IOState0::di, _IOState::uo),
      [will_not_call_mercury, promise_pure, tabled_for_io],
  "
      Globals = ML_io_user_globals;
  ").

  :- pragma foreign_proc("Java",
-    io.set_globals(Globals::di, _IOState0::di, _IOState::uo),
+    io.unsafe_set_globals(Globals::di, _IOState0::di, _IOState::uo),
      [will_not_call_mercury, promise_pure, tabled_for_io],
  "
      ML_io_user_globals = Globals;
  ").

+%-----------------------------------------------------------------------------%
+
  io.progname_base(DefaultName, PrognameBase, !IO) :-
      io.progname(DefaultName, Progname, !IO),
      PrognameBase = dir.basename_det(Progname).
@@ -5527,6 +5607,10 @@
      MR_file(mercury_stdin_binary) = stdin;
      MR_file(mercury_stdout_binary) = stdout;
  #endif
+
+#ifdef MR_THREAD_SAFE
+    pthread_mutex_init(&ML_io_user_globals_lock, MR_MUTEX_ATTR);
+#endif
  }

  ").

--------------------------------------------------------------------------
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