123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136(*
* Copyright (c) 2010 Anil Madhavapeddy <anil@recoil.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)openLwt.Infix(* General signature for all the ack modules *)moduletypeM=sigtypet(* ack: put mvar to trigger the transmission of an ack *)valt:send_ack:Sequence.tLwt_mvar.t->last:Sequence.t->t(* called when new data is received *)valreceive:t->Sequence.t->unitLwt.t(* called when new data is received *)valpushack:t->Sequence.t->unitLwt.t(* called when an ack is transmitted from elsewhere *)valtransmit:t->Sequence.t->unitLwt.tend(* Transmit ACKs immediately, the dumbest (and simplest) way *)moduleImmediate:M=structtypet={send_ack:Sequence.tLwt_mvar.t;mutablepushpending:bool;}lett~send_ack~last:_=letpushpending=falsein{send_ack;pushpending}letpushacktack_number=t.pushpending<-true;Lwt_mvar.putt.send_ackack_numberletreceivetack_number=matcht.pushpendingwith|true->Lwt.return_unit|false->pushacktack_numberlettransmitt_=t.pushpending<-false;Lwt.return_unitend(* Delayed ACKs *)moduleDelayed(Time:Mirage_time.S):M=structmoduleTT=Tcptimer.Make(Time)typedelayed_r={send_ack:Sequence.tLwt_mvar.t;mutabledelayedack:Sequence.t;mutabledelayed:bool;mutablepushpending:bool;}typet={r:delayed_r;timer:Tcptimer.t;}lettransmitacknowrack_number=Lwt_mvar.putr.send_ackack_numberlettransmitackrack_number=matchr.pushpendingwith|true->Lwt.return_unit|false->r.pushpending<-true;transmitacknowrack_numberletontimerrs=matchr.delayedwith|false->Lwt.returnTcptimer.Stoptimer|true->matchr.delayedack=swith|false->Lwt.return(Tcptimer.Continuer.delayedack)|true->r.delayed<-false;transmitackrs>>=fun()->Lwt.returnTcptimer.Stoptimerlett~send_ack~last:t=letpushpending=falseinletdelayed=falseinletdelayedack=lastinletr={send_ack;delayedack;delayed;pushpending}inletexpire=ontimerrinletperiod_ns=Duration.of_ms100inlettimer=TT.t~period_ns~expirein{r;timer}(* Advance the received ACK count *)letreceivetack_number=matcht.r.delayedwith|true->t.r.delayed<-false;transmitackt.rack_number|false->t.r.delayed<-true;t.r.delayedack<-ack_number;TT.startt.timerack_number(* Force out an ACK *)letpushacktack_number=transmitacknowt.rack_number(* Indicate that an ACK has been transmitted *)lettransmitt_=t.r.delayed<-false;t.r.pushpending<-false;Lwt.return_unitend