[mercury-users] Native garbage collector for Mercury
Thomas Charles CONWAY
conway at cs.mu.OZ.AU
Mon Sep 14 16:15:10 AEST 1998
Peter Schachte, you write:
> But in this case you don't want sequentiality. Or, looked at
> differently, you just can't express what you want logically. Let's
> say you have a character count predicate
>
> count(Buffer, Len0, Len, IO0, IO) :-
> ( is_empty(Buffer, IO0) ->
> IO = IO0,
> Len = Len0
> ; next_char(Buffer, _Char, IO0, IO1),
> count(Buffer, Len0+1, Len, IO1, IO)
> ).
>
> Remember, the buffer is being written concurrently (maybe it's a
> pipe). Suppose that is_empty/2 and next_char/4 are implemented to
> look in the io__state and do the appropriate thing if the buffer has
> characters in it or if it's empty and has been closed. If someone is
> still writing to it but we've emptied the buffer, then we suspend.
>
> So how do we write a call to count/5 that says that the buffer is to
> be filled by a different process? And how to we write the interrupt
> handler (in Mercury, no destructive C hacks please) to modify the
> io__state that count/5 is using, possibly waking count/5 if it's
> suspended waiting for input?
>
First your count code is illegal (since the final io__state from main
must be `uo' not `muo'): the call to is_empty must be deterministic.
Try code like:
:- pred count(stream(char), int, int, io__state, io__state).
:- mode count(in, in, out, di, uo) is det.
count(Buffer, Len0, Len, IO0, IO) :-
% for the sake of simplicity, we'll assume
% an eof marker.
get_item(Buffer, Char, IO0, IO1),
( Char = eof ->
IO = IO1,
Len = Len0
count(Buffer, Len0+1, Len, IO1, IO)
).
So, what is this magic thing called `stream(T)' ?
Roughly, I have a module stream.m:
:- module stream.
:- interface.
:- import_module io.
:- type stream(T).
:- pred init_stream(stream(T), io__state, io__state).
:- mode init_stream(out, di, uo) is det.
:- pred put_item(stream(T), T, io__state, io__state).
:- mode put_item(in, in, di, uo) is det.
:- pred get_item(stream(T), T, io__state, io__state).
:- mode get_item(in, out, di, uo) is det.
:- implementation.
:- import_module mutex, references, semaphore.
:- import_module queue.
:- type stream(T)
---> stream(
semaphore,
mutex,
ref(queue(T))
).
init_stream(stream(Sem, Lock, QRef)) -->
init_semaphore(Sem),
init_mutex(Lock),
{ queue__init(Q) },
new_ref(Q, QRef).
put_item(stream(Sem, Lock, QRef), Item) -->
lock(Lock),
deref(QRef, Q0),
{ queue__put(Q0, Item, Q1) },
setref(QRef, Q1),
unlock(Lock),
signal(Sem).
get_item(stream(Sem, Lock, QRef), Item) -->
wait(Sem),
lock(Lock),
deref(QRef, Q0),
( { queue__get(Q0, Item0, Q1) } ->
{ Item = Item0 },
setref(QRef, Q1),
unlock(Lock)
;
{ error("queue underflow!") }
).
The interface of references.m looks something like:
:- module references.
:- interface.
:- import_module io.
:- type ref(T).
:- pred new_ref(T, ref(T), io__state, io__state).
:- mode new_ref(in, out, di, uo) is det.
:- pred deref(ref(T), T, io__state, io__state).
:- mode deref(in, out, di, uo) is det.
:- pred setref(ref(T), T, io__state, io__state).
:- mode setref(in, in, di, uo) is det.
We can implement this in a variety of ways, but the most efficient is
to use C to implement ref(T) as a pointer to a cell containing a pointer
to the data (this data structure is sometimes known as a handle).
However, if you insist on hobbling me so I can't use C, you can use the
user data field of the io__state to hold a map from ref(T) (which we'll
just make an int) to univ. The semantics are the same, since the io__state
itself is stored in a global variable.
As for lock/unlock and signal/wait, I have code for those too (the former
is implemented in terms of the latter). Semaphores are implemented in C
in cooperation with the runtime which handles the suspension and resumption
themselves.
(For locals, have a look in /home/hydra/conway/mercury/extras/concurrency)
Thomas
--
Thomas Conway <conway at cs.mu.oz.au>
Nail here [] for new monitor. )O+
More information about the users
mailing list