123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227# 1 "src/lib/eliom_notif.server.ml"openLwt(* We use a hashtable associating resourceid to a weak set of
(userid option, notif_ev) corresponding to each tab that want to
get updates of this box.
We keep a strong reference on these data in process state.
*)moduletypeS=sigtypeidentitytypekeytypeserver_notiftypeclient_notifvalinit:unit->unitLwt.tvaldeinit:unit->unitvallisten:key->unitvalunlisten:key->unitmoduleExt:sigvalunlisten:?sitedata:Eliom_common.sitedata->([<`Client_process],[<`Data])Eliom_state.Ext.state->key->unitendvalnotify:?notfor:[`Me|`Idofidentity]->key->server_notif->unitvalclient_ev:unit->(key*client_notif)Eliom_react.Down.tvalclean:unit->unitendmoduletypeARG=sigtypeidentitytypekeytypeserver_notiftypeclient_notifvalprepare:identity->server_notif->client_notifoptionLwt.tvalequal_key:key->key->boolvalequal_identity:identity->identity->boolvalget_identity:unit->identityLwt.tvalmax_resource:intvalmax_identity_per_resource:intendmoduleMake(A:ARG):Swithtypeidentity=A.identityandtypekey=A.keyandtypeserver_notif=A.server_notifandtypeclient_notif=A.client_notif=structtypekey=A.keytypeidentity=A.identitytypeserver_notif=A.server_notiftypeclient_notif=A.client_notiftypenotification_data=A.key*A.client_notiftypenotification_react=notification_dataEliom_react.Down.t*(?step:React.step->notification_data->unit)moduleNotif_hashtbl=Hashtbl.Make(structtypet=A.keyletequal=A.equal_keylethash=Hashtbl.hashend)moduleWeak_tbl=Weak.Make(structtypet=(A.identity*notification_react)optionletequalab=matcha,bwith|None,None->true|Some(a,b),Some(c,d)->A.equal_identityac&&b==d|_->falselethash=Hashtbl.hashend)moduleI=structlettbl=Notif_hashtbl.createA.max_resourceletremove_if_emptywtkey=ifWeak_tbl.countwt=0thenNotif_hashtbl.removetblkeyletremovevkey=tryletwt=Notif_hashtbl.findtblkeyinWeak_tbl.removewtv;remove_if_emptywtkeywithNot_found->()letaddvkey=letwt=tryNotif_hashtbl.findtblkeywithNot_found->letwt=Weak_tbl.createA.max_identity_per_resourceinNotif_hashtbl.addtblkeywt;wtinifnot(Weak_tbl.memwtv)thenWeak_tbl.addwtvletiter=letiter(f:Weak_tbl.data->unitLwt.t)wt:unit=Weak_tbl.iter(fundata->Lwt.async(fun()->fdata))wtinfunfkey->tryletwt=Notif_hashtbl.findtblkeyinletgdata=matchdatawith|None->Weak_tbl.removewtdata;remove_if_emptywtkey;Lwt.return_unit|Somev->fvinitergwtwithNot_found->()endletidentity_r:(A.identity*notification_react)optionEliom_reference.Volatile.eref=Eliom_reference.Volatile.eref~scope:Eliom_common.default_process_scopeNone(* notif_e consists in a server side react event,
its client side counterpart,
and the server side function to trigger it. *)letnotif_e:notification_reactEliom_reference.Volatile.eref=Eliom_reference.Volatile.eref_from_fun~scope:Eliom_common.default_process_scope(fun()->lete,send_e=React.E.create()inletclient_ev=Eliom_react.Down.of_react(*VVV If we add throttling, some events may be lost
even if buffer size is not 1 :O *)~size:100(*VVV ? *)~scope:Eliom_common.default_process_scopeeinclient_ev,send_e)letset_identityidentity=(* For each tab connected to the app,
we keep a pointer to (identity, notif_ev) option in process state,
because the table resourceid -> (identity, notif_ev) option
is weak.
*)letnotif_e=Eliom_reference.Volatile.getnotif_einEliom_reference.Volatile.setidentity_r(Some(identity,notif_e))letset_current_identity()=A.get_identity()>>=funidentity->set_identityidentity;Lwt.return_unitletinit:unit->unitLwt.t=fun()->set_current_identity()letdeinit()=Eliom_reference.Volatile.setidentity_rNoneletlisten(key:A.key)=letidentity=Eliom_reference.Volatile.getidentity_rinI.addidentitykeyletunlisten(id:A.key)=letidentity=Eliom_reference.Volatile.getidentity_rinI.removeidentityidmoduleExt=structletunlisten?sitedata:_state(key:A.key)=letuc=Eliom_reference.Volatile.Ext.getstateidentity_rinI.removeuckeyendletnotify?notforkeycontent=letf(identity,((_,send_e)asnotif))=letblocked=matchnotforwith|Some`Me->(*TODO: fails outside of a request*)letnotif_e=Eliom_reference.Volatile.getnotif_einnotif==notif_e|Some(`Idid)->identity=id|None->falseinifblockedthenLwt.return_unitelseA.prepareidentitycontent>>=funcontent->matchcontentwith|Somecontent->send_e(key,content);Lwt.return_unit|None->Lwt.return_unitin(* on all tabs listening on this resource *)I.iterfkeyletclient_ev()=letev,_=Eliom_reference.Volatile.getnotif_einevletclean()=letfkeyweak_tbl=ifWeak_tbl.countweak_tbl=0thenNotif_hashtbl.removeI.tblkeyinNotif_hashtbl.iterfI.tblendmoduletypeARG_SIMPLE=sigtypeidentitytypekeytypenotificationvalget_identity:unit->identityLwt.tendmoduleMake_Simple(A:ARG_SIMPLE)=Make(structtypeidentity=A.identitytypekey=A.keytypeserver_notif=A.notificationtypeclient_notif=A.notificationletprepare_n=Lwt.return_somenletequal_key=(=)letequal_identity=(=)letget_identity=A.get_identityletmax_resource=1000letmax_identity_per_resource=10end)