[m-rev.] for second review: Add future datatype for concurrent and parallel programming
Paul Bone
paul at bone.id.au
Thu Oct 9 12:55:43 AEDT 2014
On Thu, Oct 09, 2014 at 11:01:15AM +1100, Julien Fischer wrote:
>
> Hi Paul,
>
> On Thu, 9 Oct 2014, Paul Bone wrote:
>> new file mode 100644
>> index 0000000..fde780d
>> --- /dev/null
>> +++ b/library/thread.future.m
>> @@ -0,0 +1,282 @@
>> +%-----------------------------------------------------------------------------%
>> +% vim: ft=mercury ts=4 sw=4 et
>> +%-----------------------------------------------------------------------------%
>> +% Copyright (C) 2014 The Mercury Team.
>> +% This file may only be copied under the terms of the GNU Library General
>> +% Public License - see the file COPYING.LIB in the Mercury distribution.
>> +%-----------------------------------------------------------------------------%
>> +%
>> +% File: thread.future.m.
>> +% Authors: pbone.
>> +% Stability: low.
>> +%
>> +% This module defines a future data type for parallel and concurrent
>> +% programming.
>
> I suggest:
>
> This module defines the data type future/1 which is useful for parallel
> and concurrent programming.
>
> ...
>
I've re-written the start of the module, could you please re-check it?
% This module defines the data types future_io/1 and future/1 which is
% useful for parallel and concurrent programming.
%
% A future represents a value that might not exist yet. A value for a
% future may be provided exactly once, but can be read any number of times.
% In these situations futures can be faster than mvars as their
% implementation is simpler: they need only one semaphore and they can avoid
% using it in some cases.
%
% There are two kinds of futures:
%
% + future(T) is a value that will be evaluated by another thread. The
% function future/1 will spawn a new thread to evaluate it's argument
% whose result can be retrieved later by calling the function wait/1.
% For example:
%
% Future = future(SomeFunction),
% ... do something in the meantime ...
% Value = wait(Future).
%
% + future_io(T) provides more flexibility, allowing the caller to control
% the creation of the thread that provides it's value. It can be used
% as follows:
%
% First:
% future(Future, !IO),
%
% Then in a separate thread:
% signal(Future, Value0, !IO),
%
% Finally, in the original thread:
% wait(Future, Value, !IO),
%
% This is more flexible because the thread can be used to signal
% multiple futures or do other things, but it requires the I/O state.
>> +
>> + % Return the value of the future, potentially blocking until the value
>> + % is available.
>
> What do you mean by "potentially" blocking? It *will* block until the value
> is available, won't it?
>
I meant that it won't block if the value is already available, but the
"until the value is available" covers this meaning. I've fixed it.
>> +
>> + % future_io/1 represents a value that may not have been computed yet.
>> + % Future values are usually computed by separate threads (using
>> + % spawn/3).
>
> s/are usually/are intended to be/
Okay.
>> + %
>> + % Technically this is a promise however promise is a reserved word in
>> + % Mercury. A promise's value can be provided by different calls to
>> + % signal on different branches of execution.
>
> I'm not sure what this last paragraph is telling me.
Rewritten:
% Elsewhere this is known as a promise. We called it future_io because
% promise is a reserved word in Mercury.
>> diff --git a/library/thread.semaphore.m b/library/thread.semaphore.m
>> index 46c53e2..14f80e1 100644
>> --- a/library/thread.semaphore.m
>> +++ b/library/thread.semaphore.m
>>
>> - % Returns a new semaphore `Sem' with its counter initialized to Count.
>> + % Sem = init(Count) returns a new semaphore `Sem' with its counter
>> + % initialized to Count.
>> %
>> -:- impure func semaphore.init(int::in) = (semaphore::uo) is det.
>> + % This has been renamed to impure_init.
>> + %
>> +:- impure func init(int::in) = (semaphore::uo) is det.
>> +:- pragma obsolete(init/1).
>
> impure_init needs to be (1) a function and (2) defined in the public interface,
> since users require a way of initialising semaphore valued mutables.
Why? If the rest of the semaphore library was pure I/O code was this ever
used outside of the standard library? I'm not if we do need to provide such
a function, at least the deprecation will allow people to continue to use
this and we can find out if it causes a problem for anyone.
> The deprecation of the function semaphore.init/1 should be mentioned in
> the NEWS file.
Okay.
>> % wait(Sem, !IO) blocks until the counter associated with `Sem'
>> % becomes greater than 0, whereupon it wakes, decrements the
>> % counter and returns.
>> %
>> -:- pred semaphore.wait(semaphore::in, io::di, io::uo) is det.
>> +:- pred wait(semaphore::in, io::di, io::uo) is det.
>>
>> % try_wait(Sem, Succ, !IO) is the same as wait/3, except that
>> % instead of blocking, it binds `Succ' to a boolean indicating
>> % whether the call succeeded in obtaining the semaphore or not.
>> %
>> -:- pred semaphore.try_wait(semaphore::in, bool::out, io::di, io::uo) is det.
>> +:- pred try_wait(semaphore::in, bool::out, io::di, io::uo) is det.
>>
>> % signal(Sem, !IO) increments the counter associated with `Sem'
>> % and if the resulting counter has a value greater than 0, it wakes
>> % one or more coroutines that are waiting on this semaphore (if
>> % any).
>
> We should probably settle on one of "thread" or "coroutine". (Since the latter
> only occurs in two places, it should most likely be the former.)
>
I agree, fixed.
>>
>> +init(Count, Semaphore, !IO) :-
>> + promise_pure (
>> + impure impure_init(Count, Semaphore)
>> + ).
>> +
>> +init(Semaphore, !IO) :-
>> + init(0, Semaphore, !IO).
>> +
>> +init(Count) = Semaphore :-
>> + impure impure_init(Count, Semaphore).
>> +
>> +signal(Semaphore, !IO) :-
>> + promise_pure (
>> + impure impure_signal(Semaphore)
>> + ).
>
> Guess what the Mercury compiler does to pure det goals with no outputs,
> such as the body of signal/3 there? ;-)
> When you're done guessing, it should be:
>
> signal(Semaphore, !IO) :-
> promise_pure (
> impure impure_signal(Semaphore),
> !:IO = !.IO
> ).
>
>> +wait(Semaphore, !IO) :-
>> + promise_pure (
>> + impure impure_wait(Semaphore)
>> + ).
>
> Ditto.
Argh, I had this error earlier and fixed it, then it regressed following the
previous reviews. Also I noticed that these goals are only optimised away
in low level C and high level C grades, not Java grades, which is
potentially confusing.
The compiler should probably warn about this.
I've fixed everything else. Thanks.
--
Paul Bone
More information about the reviews
mailing list