123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)(* *)(* A persistent hashtable *)(* *)(* Frédéric Besson (Inria Rennes) 2009-2014 *)(* *)(************************************************************************)moduletypePHashtable=sig(* see documentation in [persistent_cache.mli] *)type'attypekeyvalopen_in:string->'atvalfind:'at->key->'avaladd:'at->key->'a->unitvalmemo:string->(key->'a)->key->'avalmemo_cond:string->(key->bool)->(key->'a)->key->'aendopenHashtblmodulePHashtable(Key:HashedType):PHashtablewithtypekey=Key.t=structopenUnixtypekey=Key.tmoduleTable:sigtype'atvalempty:'atvaladd:int->'a->'at->'atvalfind:int->'at->'alistvalfold:(int->'a->'b->'b)->'at->'b->'bend=structtype'at='alistInt.Map.tletempty=Int.Map.emptyletaddhpostab=tryInt.Map.modifyh(fun_l->pos::l)tabwithNot_found->Int.Map.addh[pos]tabletfoldftabaccu=letfoldhlaccu=List.fold_left(funaccupos->fhposaccu)acculinInt.Map.foldfoldtabacculetfindhtab=Int.Map.findhtabend(* A mapping key hash -> file position *)type'adata={pos:int;mutableobj:(Key.t*'a)option}type'at={outch:out_channel;mutablehtbl:'adataTable.t;file:string}(* XXX: Move to Fun.protect once in Ocaml 4.08 *)letfun_protect~(finally:unit->unit)work=letfinally_no_exn()=letexceptionFinally_raisedofexnintryfinally()withe->letbt=Printexc.get_raw_backtrace()inPrintexc.raise_with_backtrace(Finally_raisede)btinmatchwork()with|result->finally_no_exn();result|exceptionwork_exn->letwork_bt=Printexc.get_raw_backtrace()infinally_no_exn();Printexc.raise_with_backtracework_exnwork_btletskip_blobch=lethd=Bytes.createMarshal.header_sizeinlet()=really_inputchhd0Marshal.header_sizeinletlen=Marshal.data_sizehd0inletpos=pos_inchinseek_inch(pos+len)letread_key_eleminch=matchinput_binary_intinchwith|hash->letpos=pos_ininchinlet()=skip_blobinchinSome(hash,pos)|exceptionEnd_of_file->None(**
We used to only lock/unlock regions.
Is-it more robust/portable to lock/unlock a fixed region e.g. [0;1]?
In case of locking failure, the cache is not used.
**)typelock_kind=Read|Writeletlockkdfd=letpos=lseekfd0SEEK_CURinletsuccess=tryignore(lseekfd0SEEK_SET);letlk=matchkdwithRead->F_RLOCK|Write->F_LOCKinlockffdlk1;truewithUnix.Unix_error(_,_,_)->falseinignore(lseekfdposSEEK_SET);successletunlockfd=letpos=lseekfd0SEEK_CURinlet()=tryignore(lseekfd0SEEK_SET);lockffdF_ULOCK1withUnix.Unix_error(_,_,_)->(* Here, this is really bad news --
there is a pending lock which could cause a deadlock.
Should it be an anomaly or produce a warning ?
*)ignore(lseekfdposSEEK_SET)in()(* We make the assumption that an acquired lock can always be released *)letdo_under_lockkdfdf=iflockkdfdthenfun_protectf~finally:(fun()->unlockfd)elsef()letfopen_in=open_inletopen_in(typea)f:at=letflags=[O_RDONLY;O_CREAT]inletfinch=openfilefflags0o666inletinch=in_channel_of_descrfinchinletexceptionInvalidTableFormatofadataTable.tinletrecxloadtable=matchread_key_eleminchwith|None->table|Some(hash,pos)->xload(Table.addhash{pos;obj=None}table)|exceptionewhenCErrors.noncriticale->raise(InvalidTableFormattable)intry(* Locking of the (whole) file while reading *)lethtbl=do_under_lockReadfinch(fun()->xloadTable.empty)inlet()=close_in_noerrinchinletoutch=out_channel_of_descr(openfilef[O_WRONLY;O_APPEND;O_CREAT]0o666)in{outch;file=f;htbl}withInvalidTableFormathtbl->(* The file is corrupted *)letfoldhashdataaccu=let()=seek_ininchdata.posinmatchMarshal.from_channelinchwith|(k,v)->(hash,k,v)::accu|exceptione->accuin(* Try to salvage what we can *)letdata=do_under_lockReadfinch(fun()->Table.foldfoldhtbl[])inlet()=close_in_noerrinchinletflags=[O_WRONLY;O_TRUNC;O_CREAT]inletout=openfilefflags0o666inletoutch=out_channel_of_descroutinletfoldhtbl(h,k,e)=let()=output_binary_intoutchhinletpos=pos_outoutchinlet()=Marshal.to_channeloutch(k,e)[]inTable.addh{pos;obj=None}htblinletdump()=lethtbl=List.fold_leftfoldTable.emptydatainlet()=flushoutchinhtblinlethtbl=do_under_lockWriteoutdumpin{outch;htbl;file=f}letaddtke=let{outch}=tinletfd=descr_of_out_channeloutchinleth=Key.hashkland0x7FFFFFFFinletdump()=let()=output_binary_intoutchhinletpos=pos_outoutchinlet()=Marshal.to_channeloutch(k,e)[]inlet()=flushoutchinposinletpos=do_under_lockWritefddumpint.htbl<-Table.addh{pos;obj=Some(k,e)}t.htblletfindtk=let{outch;htbl=tbl}=tinleth=Key.hashkland0x7FFFFFFFinletlpos=Table.findhtblin(* First look for already live data *)letfinddata=matchdata.objwith|Some(k',v)->ifKey.equalkk'thenSomevelseNone|None->NoneinmatchCList.find_mapfindlposwith|res->res|exceptionNot_found->(* Otherwise perform I/O and look at the disk cache *)letlpos=List.filter(fundata->Option.is_emptydata.obj)lposinlet()=ifCList.is_emptylposthenraiseNot_foundinletch=fopen_int.fileinletfinddata=let()=seek_inchdata.posinmatchMarshal.from_channelchwith|(k',v)->ifKey.equalkk'then(* Store the data in memory *)let()=data.obj<-Some(k,v)inSomevelseNone|exception_->Noneinletlookup()=CList.find_mapfindlposinletres=do_under_lockRead(descr_of_out_channeloutch)lookupinlet()=close_in_noerrchinresletmemocachef=lettbl=lazy(trySome(open_incache)with_->None)infunx->matchLazy.forcetblwith|None->fx|Sometbl->(tryfindtblxwithNot_found->letres=fxinaddtblxres;res)letmemo_condcachecondf=lettbl=lazy(trySome(open_incache)with_->None)infunx->matchLazy.forcetblwith|None->fx|Sometbl->ifcondxthenbegintryfindtblxwithNot_found->letres=fxinaddtblxres;resendelsefxend(* Local Variables: *)(* coding: utf-8 *)(* End: *)