Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file oBus_resolver.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194(*
* oBus_resolver.ml
* ----------------
* Copyright : (c) 2008, Jeremie Dimino <jeremie@dimino.org>
* Licence : BSD3
*
* This file is a part of obus, an ocaml implementation of D-Bus.
*)letsection=Lwt_log.Section.make"obus(resolver)"openLwt_reactmoduleString_map=Map.Make(String)(* We keep track on each connection of the last [cache_size] peers
that have already exited: *)letcache_size=100typeresolver={mutablecount:int;(* Number of instances of this resolver. The resolver is
automatically disabled when this number reach 0. *)owner:OBus_name.bussignal;(* The owner of the name that is being monitored. *)set_owner:OBus_name.bus->unit;(* Sets the owner. *)}(* Informations stored in connections *)andinfo={mutableresolvers:(resolver*Lwt_switch.t)Lwt.tString_map.t;(* Mapping from names to active resolvers. The maps hold thread
instead of resolver directly to avoid the following problem:
1 - a resolver for a certain name is being created,
2 - the creation yields,
3 - another resolver for the same name is requested before the
creation of the previous one terminates,
4 - the second to register in this map wwill erase the first one.
*)mutableexited:OBus_name.busarray;(* Array holding the last [cache_size] peers that have already
exited *)mutableexited_index:int;(* Position where to store the next exited peers in [exited]. *)}letfinaliseremove_=ignore(Lazy.forceremove)lethas_exitedpeer_nameinfo=letrecloopindex=ifindex=cache_sizethenfalseelseifinfo.exited.(index)=peer_namethentrueelseloop(index+1)inloop0letkey=OBus_connection.new_key()letget_name_ownerconnectionname=try%lwtOBus_connection.method_call~connection~destination:OBus_protocol.bus_name~path:OBus_protocol.bus_path~interface:OBus_protocol.bus_interface~member:"GetNameOwner"~i_args:(OBus_value.C.seq1OBus_value.C.basic_string)~o_args:(OBus_value.C.seq1OBus_value.C.basic_string)namewithexnwhenOBus_error.nameexn="org.freedesktop.DBus.Error.NameHasNoOwner"->Lwt.return""(* Handle NameOwnerChanged events *)letupdate_mappinginfomessage=letopenOBus_messageinletopenOBus_valueinmatchmessagewith|{sender="org.freedesktop.DBus";typ=Signal(["org";"freedesktop";"DBus"],"org.freedesktop.DBus","NameOwnerChanged");body=[V.Basic(V.Stringname);V.Basic(V.Stringold_owner);V.Basic(V.Stringnew_owner)]}->ifOBus_name.is_uniquename&&new_owner=""&¬(has_exitednameinfo)thenbegin(* Remember that the peer has exited: *)info.exited.(info.exited_index)<-name;info.exited_index<-(info.exited_index+1)modcache_sizeend;beginmatchtryLwt.state(String_map.findnameinfo.resolvers)withNot_found->Sleepwith|Return(resolver,switch)->resolver.set_ownernew_owner|Fail_|Sleep->(* Discards events arriving before GetNameOwner has returned *)()end;Somemessage|_->Somemessageletmake?switchconnectionname=Lwt_switch.checkswitch;OBus_string.assert_validateOBus_name.validate_busname;letinfo=matchOBus_connection.getconnectionkeywith|Someinfo->info|None->letinfo={resolvers=String_map.empty;exited=Array.makecache_size"";exited_index=0;}inOBus_connection.setconnectionkey(Someinfo);let_=Lwt_sequence.add_l(update_mappinginfo)(OBus_connection.incoming_filtersconnection)ininfoin(* If [name] is a unique name and the peer has already exited, then
there is nothing to do: *)ifOBus_name.is_uniquename&&has_exitednameinfothenLwt.return(S.const"")elsebeginlet%lwtresolver,export_switch=matchtrySome(String_map.findnameinfo.resolvers)withNot_found->Nonewith|Somethread->thread|None->letwaiter,wakener=Lwt.wait()ininfo.resolvers<-String_map.addnamewaiterinfo.resolvers;letexport_switch=Lwt_switch.create()intry%lwtlet%lwt()=OBus_match.export~switch:export_switchconnection(OBus_match.rule~typ:`Signal~sender:OBus_protocol.bus_name~interface:OBus_protocol.bus_interface~member:"NameOwnerChanged"~path:OBus_protocol.bus_path~arguments:(OBus_match.make_arguments[(0,OBus_match.AF_stringname)])())inlet%lwtcurrent_owner=get_name_ownerconnectionnameinletowner,set_owner=S.createcurrent_ownerinletresolver={count=0;owner;set_owner}inLwt.wakeupwakener(resolver,export_switch);Lwt.return(resolver,export_switch)withexn->info.resolvers<-String_map.removenameinfo.resolvers;Lwt.wakeup_exnwakenerexn;let%lwt()=Lwt_switch.turn_offexport_switchinLwt.failexninresolver.count<-resolver.count+1;letremove=lazy(try%lwtresolver.count<-resolver.count-1;ifresolver.count=0thenbegin(* The resolver is no more used, so we disable it: *)info.resolvers<-String_map.removenameinfo.resolvers;Lwt_switch.turn_offexport_switchendelseLwt.return()withexn->let%lwt()=Lwt_log.warning_f~section~exn"failed to disable resolver for name %S"nameinLwt.failexn)inletowner=S.with_finaliser(finaliseremove)resolver.ownerinlet%lwt()=Lwt_switch.add_hook_or_execswitch(fun()->S.stopowner;Lazy.forceremove)inLwt.returnownerend