annotate reactive.pl @ 15:c91d53b19dbb tip

Meh?
author samer
date Wed, 19 Dec 2012 19:16:30 +0000
parents 13b598d91c15
children
rev   line source
samer@0 1 :- module(reactive,
samer@0 2 [ run_process/1
samer@0 3 , init_process/2
samer@0 4 , step_event/3
samer@0 5 , step_timeout/2
samer@0 6 , step_timeout/3
samer@0 7 , get_timeout/2
samer@0 8 , req_message/2
samer@0 9 , req_message_or_timeout/4
samer@0 10 , (>)/3
samer@0 11 ]).
samer@0 12
samer@0 13 /** <module> Tools for reactive programming
samer@0 14
samer@0 15 This module provides a framework for doing
samer@0 16 reactive programming, where a reactive process is
samer@0 17 represented by one or two continuations
samer@0 18 which define how it will react to received events or the
samer@0 19 passage of time.
samer@0 20
samer@0 21 Relevant types:
samer@0 22 ==
samer@0 23 ptail == pred(process).
samer@0 24
samer@0 25 process ---> get(term,ptail)
samer@0 26 ; gett(time,term,ptail,ptail).
samer@0 27
samer@0 28
samer@0 29 event ---> event(time,_).
samer@0 30
samer@0 31 event_handler ---> term^ptail.
samer@0 32 ==
samer@0 33
samer@0 34 The type =|ptail|= represents a slice of execution for a process.
samer@0 35 When called, code may execute until the process
samer@0 36 wishes to block waiting for an event or time-out to occur.
samer@0 37 It does this by returning a value of type =|process|=
samer@0 38 representing either a process waiting indefinitely for an event,
samer@0 39 or a processing waiting until a certain for an event.
samer@0 40
samer@0 41 Values of type time are either floats representing times as
samer@0 42 returned by get_time/1 or the atom 'inf' representing a point
samer@0 43 infinitely far in the future.
samer@0 44 */
samer@0 45
samer@0 46 :- meta_predicate req_message(:,-).
samer@0 47 :- meta_predicate req_message_or_timeout(+,:,:,-).
samer@0 48 :- meta_predicate run_process(1).
samer@0 49 :- meta_predicate init_process(1,?).
samer@0 50 :- meta_predicate >(+,1,?).
samer@0 51
samer@0 52 :- use_module(qutils).
samer@0 53
samer@0 54 %---------------------------------------
samer@0 55
samer@0 56 %% init_process( +Spec:ptail, -P:process) is det.
samer@0 57 %
samer@0 58 % Initialise process specified by Spec, which is
samer@0 59 % simply a callable term taking one argument.
samer@0 60 % P is bound to representation of sleeping
samer@0 61 % process on exit.
samer@0 62 init_process(PS,P1) :- call(PS,P1).
samer@0 63
samer@0 64 %% >(+E:event,+Spec:ptail,-P:process) is det.
samer@0 65 %
samer@0 66 % Initialise process and immediately supply an event.
samer@0 67 % This uses init_process/2 to initialise the process specified
samer@0 68 % by Spec and then use step_event/3 to process the given event.
samer@0 69 %
samer@0 70 % This implies that if Spec is of type ptail, then
samer@0 71 % Event>Spec is also of type ptail.
samer@0 72 >(Event,Spec,Proc) :-
samer@0 73 init_process(Spec,P1),
samer@0 74 step_event(Event,P1,Proc).
samer@0 75
samer@0 76 %% run_process( +Spec:ptail) is det.
samer@0 77 %
samer@0 78 % Runs process represented by Spec (see init_process/2).
samer@0 79 % After initialising, this predicate goes into a recursive procedure
samer@0 80 % waiting for events on the current thread's message queue and
samer@0 81 % passing them to the waiting process.
samer@0 82 run_process(M:Spec) :- init_process(M:Spec,Proc), cont_process(Proc).
samer@0 83
samer@0 84
samer@0 85 %% cont_process( +Proc:process) is det.
samer@0 86 %
samer@0 87 % Takes a quiescent process, waits for and then handles a new event.
samer@0 88 % (Including timeout events.)
samer@0 89 cont_process(Proc) :-
samer@0 90 get_timeout(Proc,T), !,
samer@0 91 garbage_collect_atoms,
samer@0 92 get_message_or_timeout(T,Event),
samer@0 93 event_process_cont(Event,Proc,T).
samer@0 94
samer@0 95
samer@0 96 %% event_process_cont(+Event:evdesc,+P:process,+Deadline:time) is det.
samer@0 97 %
samer@0 98 % Handle latest event Event on process Proc. The evdesc type is defined
samer@0 99 % as =|evdesc ---> quit; timeout; event(time,term)|= with the following
samer@0 100 % semantics:
samer@0 101 %
samer@0 102 % * quit
samer@0 103 % The process will terminate: this procedure succeeds and returns immediately.
samer@0 104 %
samer@0 105 % * timeout
samer@0 106 % The timeout continuation of the process is followed.
samer@0 107 %
samer@0 108 % * event(Time,Data)
samer@0 109 % If the current time is less than 0.5s after the event time,
samer@0 110 % the process's event continuation is called. Otherwise,
samer@0 111 % the event is dropped and we continue as if it had never happened.
samer@0 112 event_process_cont(quit,_,_) :- !.
samer@0 113 event_process_cont(timeout,P1,_) :- step_timeout(P1,P2), !, cont_process(P2).
samer@0 114 event_process_cont(event(ET,EData),P1,T1) :- !,
samer@0 115 get_time(Now),
samer@0 116 ( Now>ET+0.5 -> writeln('#'), cont_process(P1)
samer@0 117 ; cont_event(T1,P1,ET,EData)).
samer@0 118
samer@0 119 %% cont_event(+Deadline:time, +P:process, +EvTime:time, +EvData:term) is det.
samer@0 120 %
samer@0 121 % Handles process continuation after receiving a valid event.
samer@0 122 % If the event is later than the process timeout, the timeout
samer@0 123 % continuation is called recursively until the process is ready
samer@0 124 % to receive the current event. Then this event is handled and
samer@0 125 % the process continues from there.
samer@0 126 cont_event(T1,P1,ET,EData) :-
samer@0 127 ( T1\=inf, ET>T1
samer@0 128 -> step_timeout(P1,P2),
samer@0 129 get_timeout(P2,T2), !,
samer@0 130 cont_event(T2,P2,ET,EData)
samer@0 131 ; step_event(event(ET,EData),P1,P2), !,
samer@0 132 cont_process(P2)
samer@0 133 ).
samer@0 134
samer@0 135 cont_event_safely(T1,P1,ET,EData) :-
samer@0 136 ( T1\=inf, ET>T1
samer@0 137 -> step_timeout(P1,P2),
samer@0 138 get_timeout(P2,T2), !,
samer@0 139 cont_event_safely(T2,P2,ET,EData)
samer@0 140 ; ( step_event(event(ET,EData),P1,P2) -> true
samer@0 141 ; writeln(failed(step_event(event(ET,EData),P1,P2))), P1=P2
samer@0 142 ),
samer@0 143 cont_process(P2)
samer@0 144 ).
samer@0 145
samer@0 146 %% req_message( +EventHandler:event_handler, -P:process) is det.
samer@0 147 %
samer@0 148 % Returns process state representing quiescent process
samer@0 149 % waiting indefinitley for event. EventHandler must be
samer@0 150 % a term of the form Msg^PTail, where Msg is to be unified
samer@0 151 % with next event term before calling PTail.
samer@0 152 req_message(Mod:Temp^OnEvent, get(Temp,Mod:OnEvent)).
samer@0 153
samer@0 154 %% req_message_or_timeout( +T:time, +TimeOutHandler:ptail, +EventHandler:event_handler, -P:process) is det.
samer@0 155 %
samer@0 156 % Returns process state representing quiescent process
samer@0 157 % waiting for event. EventHandler must be
samer@0 158 % a term of the form Msg^PTail, where Msg is to be unified
samer@0 159 % with next event term before calling PTail to get the next
samer@0 160 % continuation of the process.
samer@0 161 % TimeOutHandler will be called if time no event arrives
samer@0 162 % before time T.
samer@0 163 req_message_or_timeout(T,OnTimeout,Mod:Temp^OnEvent,
samer@0 164 gett(T,Temp,Mod:OnEvent,OnTimeout)).
samer@0 165
samer@0 166 leq(inf,T2) :- !, T2=inf.
samer@0 167 leq(T1,T2) :- !, (T2=inf -> true; T1=<T2).
samer@0 168
samer@0 169 %% get_timeout(+P:process,-Deadline:time) is det
samer@0 170 %
samer@0 171 % Get timeout time of quiescent process. May be inf.
samer@0 172 get_timeout(get(_,_),inf).
samer@0 173 get_timeout(gett(T,_,_,_),T).
samer@0 174
samer@0 175 %% step_timeout(+P1:process,-P2:process) is det.
samer@0 176 %
samer@0 177 % Wake up process P1 with timeout event and run until
samer@0 178 % it blocks at P2.
samer@0 179 step_timeout(gett(_,_,_,OnTimeout),P2) :- call(OnTimeout,P2).
samer@0 180 step_timeout(get(E,OnEvent),get(E,OnEvent)).
samer@0 181
samer@0 182 %% step_timeout(+T:time, +P1:process,-P2:process) is det.
samer@0 183 %
samer@0 184 % If time T is later than P1's timeout time, wake it up and
samer@0 185 % run timeout event until it blocks at P2. Otherwise, P2=P1,
samer@0 186 % ie process is still waiting.
samer@0 187 step_timeout(T,P1,P2) :-
samer@0 188 get_timeout(P1,T1),
samer@0 189 (leq(T1,T) -> step_timeout(P1,P2); P1=P2).
samer@0 190
samer@3 191 %:- index(step_event(0,1,0)).
samer@0 192
samer@0 193 %% step_event(+E:event, +P1:process, -P2:process) is det.
samer@0 194 %
samer@0 195 % Wake up process P1 with event E and run until blocking at P2.
samer@0 196 step_event(E,get(E,OnEvent),P2) :- call(OnEvent,P2).
samer@0 197 step_event(E,gett(_,E,OnEvent,_),P2) :- call(OnEvent,P2).