-- ----------------------------------------------------------------------------{- |
Module : Holumbus.Distribution.DMVar
Copyright : Copyright (C) 2009 Stefan Schmidt
License : MIT
Maintainer : Stefan Schmidt (stefanschmidt@web.de)
Stability : experimental
Portability: portable
Version : 0.1
This module offers the distributed MVar datatype.
The datatype behaves just like a normal MVar, but the content of the
variable may be stored on a different DNode. When accessing the DMVar,
the content will be fetched from the external node and written back.
It is guaranteed, that only one node at a time can take the content
of the DMVar. Just like normal DMVars, you can produce deadlocks.
When a node dies which holds the content of a DMVar, the node which
created the variable will reset its value to the last known value.
If the owner dies, the other nodes cannot access the content of the
DMVar any more.
-}-- ----------------------------------------------------------------------------moduleHolumbus.Distribution.DMVar(-- * datatypeDMVar-- * creating and closing DMVars,newDMVar,newEmptyDMVar,newRemoteDMVar,closeDMVar-- * acccessing DMVars,readDMVar,takeDMVar,putDMVar)whereimportPreludehiding(catch)importControl.Concurrent.MVarimportData.BinaryimportqualifiedData.ByteString.LazyasBimportSystem.IOimportSystem.Log.LoggerimportHolumbus.Distribution.DNode.BaselocalLogger::StringlocalLogger="Holumbus.Distribution.DMVar"dMVarType::DResourceTypedMVarType=mkDResourceType"DMVAR"mkDMVarEntry::(Binarya)=>DMVarReferencea->DResourceEntrymkDMVarEntryd=DResourceEntry{dre_Dispatcher=dispatchDMVarRequestd}dataDMVarRequestMessage=DVMReqRead|DVMReqTake|DVMReqPutB.ByteStringderiving(Show)instanceBinaryDMVarRequestMessagewhereput(DVMReqRead)=putWord81put(DVMReqTake)=putWord82put(DVMReqPutb)=putWord83>>putbget=dot<-getWord8casetof1->return(DVMReqRead)2->return(DVMReqTake)3->get>>=\b->return(DVMReqPutb)_->error"DMVarRequestMessage: wrong encoding"dataDMVarResponseMessage=DVMRspReadB.ByteString|DVMRspTakeB.ByteString|DVMRspPutderiving(Show)instanceBinaryDMVarResponseMessagewhereput(DVMRspReadb)=putWord81>>putbput(DVMRspTakeb)=putWord82>>putbput(DVMRspPut)=putWord83get=dot<-getWord8casetof1->get>>=\b->return(DVMRspReadb)2->get>>=\b->return(DVMRspTakeb)3->return(DVMRspPut)_->error"DMVarResponseMessage: wrong encoding"dispatchDMVarRequest::(Binarya)=>DMVarReferencea->DNodeId->Handle->IO()dispatchDMVarRequestdchdnahdl=dodebugMlocalLogger"dispatcher: getting message from handle"raw<-getByteStringMessagehdlletmsg=(decoderaw)debugMlocalLogger$"dispatcher: Message: "++showmsgcasemsgof(DVMReqRead)->handleReaddchhdl(DVMReqTake)->handleTakedchdnahdl(DVMReqPutb)->handlePutdch(decodeb)hdl-- | The DMVar datatype.dataDMVara=DMVarLocalDResourceAddress(MVara)(MVar(a,MaybeDHandlerId))|DMVarRemoteDResourceAddressinstanceBinary(DMVara)whereput(DMVarLocaldra__)=putdraput(DMVarRemotedra)=putdraget=get>>=\dra->return(DMVarRemotedra)dataDMVarReferencea=DMVarReferenceDResourceAddress(MVara)(MVar(a,MaybeDHandlerId))-- | Creates a new local DMVar with a start value.-- The string parameter specifies the name of the variable.-- If you leave it empty, a random value will be generated.newDMVar::(Binarya)=>String->a->IO(DMVara)newDMVarsd=dodra<-genLocalResourceAddressdMVarTypesv<-newMVardo<-newEmptyMVarletdmv=(DMVarLocaldravo)dvr=(DMVarReferencedravo)dve=(mkDMVarEntrydvr)addLocalResourcedradvereturndmv-- | Creates a new empty local DMVar. The string parameter specifies the name of-- the variable. If you leave it empty, a random value will be generated.newEmptyDMVar::(Binarya)=>String->IO(DMVara)newEmptyDMVars=dodra<-genLocalResourceAddressdMVarTypesv<-newEmptyMVaro<-newMVar(undefined,Nothing)letdmv=(DMVarLocaldravo)dvr=(DMVarReferencedravo)dve=(mkDMVarEntrydvr)addLocalResourcedradvereturndmv-- | Creates a reference to an external DMVar.-- The first parameter is the name of the resource and the second one-- the name of the node.newRemoteDMVar::String->String->IO(DMVara)newRemoteDMVarrn=doreturn$DMVarRemotedrawheredra=mkDResourceAddressdMVarTypern-- | Closes a DMVarcloseDMVar::(DMVara)->IO()closeDMVar(DMVarLocaldra__)=dodelLocalResourcedracloseDMVar(DMVarRemotedra)=dodelForeignResourcedrarequestRead::(Binarya)=>Handle->IOarequestReadhdl=doputByteStringMessage(encode$DVMReqRead)hdlraw<-getByteStringMessagehdlletrsp=(decoderaw)caserspof(DVMRspReadd)->return$decoded_->error"DMVar - requestRead: invalid response"handleRead::(Binarya)=>DMVarReferencea->Handle->IO()handleRead(DMVarReference_v_)hdl=doa<-readMVarvputByteStringMessage(encode$DVMRspRead$encodea)hdlrequestTake::(Binarya)=>Handle->IOarequestTakehdl=doputByteStringMessage(encode$DVMReqTake)hdlraw<-getByteStringMessagehdlletrsp=(decoderaw)caserspof(DVMRspTaked)->return$decoded_->error"DMVar - requestTake: invalid response"handleTake::(Binarya)=>DMVarReferencea->DNodeId->Handle->IO()handleTaker@(DMVarReference_vo)dnihdl=dodebugMlocalLogger$"handleTake: 1"a<-takeMVarvdebugMlocalLogger$"handleTake: 2"-- install handler and save backupmbDhi<-addForeignDNodeHandlerFalsedni(handleErrorTaker)debugMlocalLogger$"handleTake: 3"putMVaro(a,mbDhi)debugMlocalLogger$"handleTake: 4"putByteStringMessage(encode$DVMRspTake$encodea)hdldebugMlocalLogger$"handleTake: 5"handleErrorTake::(Binarya)=>DMVarReferencea->DHandlerId->IO()handleErrorTake(DMVarReference_vo)dhi=dodebugMlocalLogger$"handleErrorTake: 1"(a,_)<-takeMVarodelForeignHandlerdhidebugMlocalLogger$"handleErrorTake: 2"putMVarvarequestPut::(Binarya)=>a->Handle->IO()requestPutdhdl=doputByteStringMessage(encode$DVMReqPut$encoded)hdlraw<-getByteStringMessagehdlletrsp=(decoderaw)caserspof(DVMRspPut)->return()_->error"DMVar - requestWrite: invalid response"handlePut::(Binarya)=>DMVarReferencea->a->Handle->IO()handlePut(DMVarReference_vo)ahdl=do-- delete backup and kill handler (_,mbDhi)<-takeMVarocasembDhiof(Justdhi)->delForeignHandlerdhi(Nothing)->return()putMVarvaputByteStringMessage(encode$DVMRspPut)hdl-- | Reads the content of a DMVar. Blocks if the Variable is empty.-- This may throw an exception if the owner of the variable is unreachable.readDMVar::(Binarya)=>DMVara->IOareadDMVar(DMVarLocal_v_)=doreadMVarvreadDMVar(DMVarRemotea)=dounsafeAccessForeignResourcearequestRead-- | Takes the content of a DMVar. Blocks if the Variable is empty.-- This may throw an exception if the owner of the variable is unreachable.takeDMVar::(Binarya)=>DMVara->IOatakeDMVar(DMVarLocal_vo)=doa<-takeMVarvputMVaro(a,Nothing)returnatakeDMVar(DMVarRemotea)=dounsafeAccessForeignResourcearequestTake-- | Writes a value to the DMvar. Blocks if the Variable is not empty.-- This may throw an exception if the owner of the variable is unreachable.putDMVar::(Binarya)=>DMVara->a->IO()putDMVar(DMVarLocal_vo)d=do(_,mbDhi)<-takeMVarocasembDhiof(Justdhi)->delForeignHandlerdhi(Nothing)->return()putMVarvdputDMVar(DMVarRemotea)d=dounsafeAccessForeignResourcea(requestPutd)