summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Announcer/Tox.hs102
-rw-r--r--ToxManager.hs4
-rw-r--r--ToxToXMPP.hs6
-rw-r--r--examples/dhtd.hs20
4 files changed, 88 insertions, 44 deletions
diff --git a/Announcer/Tox.hs b/Announcer/Tox.hs
index c9164d22..f79f8ee7 100644
--- a/Announcer/Tox.hs
+++ b/Announcer/Tox.hs
@@ -50,23 +50,16 @@ data AnnounceMethod r = forall nid ni sr addr tok a.
50 -- already in progress at announce time. Repeated searches are 50 -- already in progress at announce time. Repeated searches are
51 -- likely to finish faster than the first since nearby nodes 51 -- likely to finish faster than the first since nearby nodes
52 -- are not discarded. 52 -- are not discarded.
53 , aPublish :: Either (r -> sr -> IO ()) 53 , aPublish :: r -> tok -> Maybe ni -> IO (Maybe a)
54 (r -> tok -> Maybe ni -> IO (Maybe a))
55 -- ^ The action to perform when we find nearby nodes. The 54 -- ^ The action to perform when we find nearby nodes. The
56 -- destination node is given as a Maybe so that methods that 55 -- destination node is given as a Maybe so that methods that
57 -- treat 'Nothing' as loop-back address can be passed here, 56 -- treat 'Nothing' as loop-back address can be passed here,
58 -- however 'Nothing' will not be passed by the announcer 57 -- however 'Nothing' will not be passed by the announcer
59 -- thread. 58 -- thread.
60 -- 59 --
61 -- There are two cases: 60 -- The action requires a "token" from the destination
62 -- 61 -- node. This is the more typical "announce" semantics for
63 -- [Left] The action to perform requires a search result. 62 -- Kademlia.
64 -- This was implemented to support Tox's DHTKey and
65 -- Friend-Request messages.
66 --
67 -- [Right] The action requires a "token" from the destination
68 -- node. This is the more typical "announce" semantics for
69 -- Kademlia.
70 , aNearestNodes :: nid -> STM [ni] 63 , aNearestNodes :: nid -> STM [ni]
71 -- ^ Method to obtain starting nodes from an iterative Kademlia search. 64 -- ^ Method to obtain starting nodes from an iterative Kademlia search.
72 , aTarget :: nid 65 , aTarget :: nid
@@ -80,6 +73,38 @@ data AnnounceMethod r = forall nid ni sr addr tok a.
80 -- use the closest nodes found so far. 73 -- use the closest nodes found so far.
81 } 74 }
82 75
76-- | This type specifies a Kademlia search and an action to perform upon the result.
77data SearchMethod r = forall nid ni sr addr tok a.
78 ( Show nid
79 , Hashable nid
80 , Hashable ni
81 , Ord addr
82 , Ord nid
83 , Ord ni
84 ) => SearchMethod
85 { sSearch :: Search nid addr tok ni sr
86 -- ^ This is the Kademlia search to run repeatedly to find the
87 -- nearby nodes. A new search is started whenever one is not
88 -- already in progress at announce time. Repeated searches are
89 -- likely to finish faster than the first since nearby nodes
90 -- are not discarded.
91 --
92 -- XXX: Currently, "repeatedly" is wrong.
93 , sWithResult :: r -> sr -> IO ()
94 -- ^
95 -- The action to perform upon a search result. This was
96 -- implemented to support Tox's DHTKey and Friend-Request
97 -- messages.
98 , sNearestNodes :: nid -> STM [ni]
99 -- ^ Method to obtain starting nodes from an iterative Kademlia search.
100 , sTarget :: nid
101 -- ^ This is the Kademlia node-id of the item being announced.
102 , sInterval :: POSIXTime
103 -- ^ The time between searches.
104 --
105 -- XXX: Currently, search results will stop any repetition.
106 }
107
83 108
84-- announcement started: 109-- announcement started:
85newAnnouncement :: STM (IO a) 110newAnnouncement :: STM (IO a)
@@ -115,17 +140,15 @@ reAnnounce checkFin announce interval = \announcer k now -> do
115 announce 140 announce
116 141
117-- | Schedule a recurring Search + Announce sequence. 142-- | Schedule a recurring Search + Announce sequence.
118schedule :: Announcer -> AnnounceKey -> AnnounceMethod r -> r -> IO () 143scheduleAnnounce :: Announcer -> AnnounceKey -> AnnounceMethod r -> r -> IO ()
119schedule announcer k AnnounceMethod{aSearch,aPublish,aNearestNodes,aTarget,aInterval} r = do 144scheduleAnnounce announcer k AnnounceMethod{aSearch,aPublish,aNearestNodes,aTarget,aInterval} r = do
120 st <- atomically $ newSearch aSearch aTarget [] 145 st <- atomically $ newSearch aSearch aTarget []
121 ns <- atomically $ newTVar MM.empty 146 ns <- atomically $ newTVar MM.empty
122 let astate = AnnounceState st ns 147 let astate = AnnounceState st ns
123 publishToNodes is 148 publishToNodes is = do
124 | Left _ <- aPublish = return ()
125 | Right publish <- aPublish = do
126 forM_ is $ \(Binding ni mtok _) -> do 149 forM_ is $ \(Binding ni mtok _) -> do
127 forM_ mtok $ \tok -> do 150 forM_ mtok $ \tok -> do
128 got <- publish r tok (Just ni) 151 got <- aPublish r tok (Just ni)
129 now <- getPOSIXTime 152 now <- getPOSIXTime
130 forM_ got $ \_ -> do 153 forM_ got $ \_ -> do
131 atomically $ modifyTVar ns $ MM.insertTake announceK ni (Down now) 154 atomically $ modifyTVar ns $ MM.insertTake announceK ni (Down now)
@@ -134,20 +157,7 @@ schedule announcer k AnnounceMethod{aSearch,aPublish,aNearestNodes,aTarget,aInte
134 bs <- readTVar (searchInformant st {- :: TVar (MinMaxPSQ' ni nid tok -}) 157 bs <- readTVar (searchInformant st {- :: TVar (MinMaxPSQ' ni nid tok -})
135 return $ MM.toList bs 158 return $ MM.toList bs
136 publishToNodes is 159 publishToNodes is
137 onResult sr 160 onResult sr = return True
138 | Right _ <- aPublish = return True
139 | Left sendit <- aPublish = do
140 scheduleImmediately announcer k $ ScheduledItem $ \_ _ _ -> return $ do
141 got <- sendit r sr
142 -- If we had a way to get the source of a search result, we might want to
143 -- treat it similarly to an announcing node and remember it in the 'aStoringNodes'
144 -- MinMaxPSQ. For now, I'm just letting the nodes for which we've already sent
145 -- a message be forgotten.
146 --
147 -- forM_ got $ \_ -> do
148 -- atomically $ modifyTVar ns $ MM.insertTake announceK ni (Down now)
149 return ()
150 return True -- True to keep searching.
151 searchAgain = do 161 searchAgain = do
152 -- Canceling a pending search here seems to make announcements more reliable. 162 -- Canceling a pending search here seems to make announcements more reliable.
153 searchCancel st 163 searchCancel st
@@ -172,3 +182,33 @@ schedule announcer k AnnounceMethod{aSearch,aPublish,aNearestNodes,aTarget,aInte
172 atomically $ scheduleImmediately announcer k $ ScheduledItem (newAnnouncement searchAgain search announce aInterval) 182 atomically $ scheduleImmediately announcer k $ ScheduledItem (newAnnouncement searchAgain search announce aInterval)
173 interruptDelay (interrutible announcer) 183 interruptDelay (interrutible announcer)
174 184
185-- | Schedule a recurring Search + Publish sequence.
186scheduleSearch :: Announcer -> AnnounceKey -> SearchMethod r -> r -> IO ()
187scheduleSearch announcer k SearchMethod{sSearch,sWithResult,sNearestNodes,sTarget,sInterval} r = do
188 st <- atomically $ newSearch sSearch sTarget []
189 ns <- atomically $ newTVar MM.empty
190 let astate = AnnounceState st ns
191 onResult sr = do
192 -- XXX: Using /k/ here as the announce key is causing the search not to repeat.
193 scheduleImmediately announcer k $ ScheduledItem $ \_ _ _ -> return $ do
194 got <- sWithResult r sr
195 -- If we had a way to get the source of a search result, we might want to
196 -- treat it similarly to an announcing node and remember it in the 'aStoringNodes'
197 -- MinMaxPSQ. For now, I'm just letting the nodes for which we've already sent
198 -- a message be forgotten.
199 --
200 -- forM_ got $ \_ -> do
201 -- atomically $ modifyTVar ns $ MM.insertTake announceK ni (Down now)
202 return ()
203 return True -- True to keep searching.
204 searchAgain = do
205 -- Canceling a pending search here seems to make announcements more reliable.
206 searchCancel st
207 isfin <- searchIsFinished st -- Always True, since we canceled.
208 return $ when isfin $ void $ fork search
209 search = do -- thread to fork
210 atomically $ reset sNearestNodes sSearch sTarget st
211 searchLoop sSearch sTarget onResult st
212 atomically $ scheduleImmediately announcer k $ ScheduledItem (newAnnouncement searchAgain search (return ()) sInterval)
213 interruptDelay (interrutible announcer)
214
diff --git a/ToxManager.hs b/ToxManager.hs
index af1911d4..1e9c618d 100644
--- a/ToxManager.hs
+++ b/ToxManager.hs
@@ -81,10 +81,10 @@ toxman announcer toxbkts tox presence = ToxManager
81 -- Schedule recurring announce. 81 -- Schedule recurring announce.
82 -- 82 --
83 akey <- atomically $ packAnnounceKey announcer $ "toxid:" ++ show pubid 83 akey <- atomically $ packAnnounceKey announcer $ "toxid:" ++ show pubid
84 schedule announcer 84 scheduleAnnounce announcer
85 akey 85 akey
86 (AnnounceMethod (toxQSearch tox) 86 (AnnounceMethod (toxQSearch tox)
87 (Right $ toxAnnounceSendData tox) 87 (toxAnnounceSendData tox)
88 nearNodes 88 nearNodes
89 pubid 89 pubid
90 toxAnnounceInterval) 90 toxAnnounceInterval)
diff --git a/ToxToXMPP.hs b/ToxToXMPP.hs
index ac24ce6d..ad5cb0dd 100644
--- a/ToxToXMPP.hs
+++ b/ToxToXMPP.hs
@@ -165,10 +165,10 @@ forkAccountWatcher acc tox st announcer = forkIO $ do
165 -- likelihood of failure as the chances of packet loss 165 -- likelihood of failure as the chances of packet loss
166 -- happening to all (up to to 8) packets sent is low. 166 -- happening to all (up to to 8) packets sent is low.
167 -- 167 --
168 schedule announcer 168 scheduleSearch announcer
169 akey 169 akey
170 (AnnounceMethod (toxQSearch tox) 170 (SearchMethod (toxQSearch tox)
171 (Left $ \theirkey rendezvous -> do 171 (\theirkey rendezvous -> do
172 dkey <- Tox.getContactInfo tox 172 dkey <- Tox.getContactInfo tox
173 sendMessage 173 sendMessage
174 (Tox.toxToRoute tox) 174 (Tox.toxToRoute tox)
diff --git a/examples/dhtd.hs b/examples/dhtd.hs
index 28bfc9b4..7cf3393c 100644
--- a/examples/dhtd.hs
+++ b/examples/dhtd.hs
@@ -1009,10 +1009,14 @@ clientSession s@Session{..} sock cnum h = do
1009 (const method) 1009 (const method)
1010 announceSendData) 1010 announceSendData)
1011 dhtQuery 1011 dhtQuery
1012 doit :: Char -> Announcer -> AnnounceKey -> AnnounceMethod r -> r -> IO () 1012 doitR :: Char -> Announcer -> AnnounceKey -> AnnounceMethod r -> r -> IO ()
1013 doit '+' = schedule 1013 doitR '+' = scheduleAnnounce
1014 doit '-' = \a k _ _ -> cancel a k 1014 doitR '-' = \a k _ _ -> cancel a k
1015 doit _ = \_ _ _ _ -> hPutClientChunk h "Starting(+) or canceling(-)?" 1015 doitR _ = \_ _ _ _ -> hPutClientChunk h "Starting(+) or canceling(-)?"
1016 doitL :: Char -> Announcer -> AnnounceKey -> SearchMethod r -> r -> IO ()
1017 doitL '+' = scheduleSearch
1018 doitL '-' = \a k _ _ -> cancel a k
1019 doitL _ = \_ _ _ _ -> hPutClientChunk h "Starting(+) or canceling(-)?"
1016 matchingResult :: 1020 matchingResult ::
1017 ( Typeable stok 1021 ( Typeable stok
1018 , Typeable ptok 1022 , Typeable ptok
@@ -1046,9 +1050,9 @@ clientSession s@Session{..} sock cnum h = do
1046 dta <- either (const Nothing) Just $ announceParseData dtastr 1050 dta <- either (const Nothing) Just $ announceParseData dtastr
1047 return $ do 1051 return $ do
1048 akey <- atomically $ packAnnounceKey announcer (method ++ ":" ++ dtastr) 1052 akey <- atomically $ packAnnounceKey announcer (method ++ ":" ++ dtastr)
1049 doit op announcer 1053 doitR op announcer
1050 akey 1054 akey
1051 (AnnounceMethod qsearch (Right asend) 1055 (AnnounceMethod qsearch asend
1052 (\nid -> R.kclosest (searchSpace qsearch) 1056 (\nid -> R.kclosest (searchSpace qsearch)
1053 searchK 1057 searchK
1054 nid 1058 nid
@@ -1072,9 +1076,9 @@ clientSession s@Session{..} sock cnum h = do
1072 pub <- selectedKey 1076 pub <- selectedKey
1073 return $ do 1077 return $ do
1074 akey <- atomically $ packAnnounceKey announcer (method ++ ":" ++ dtastr) 1078 akey <- atomically $ packAnnounceKey announcer (method ++ ":" ++ dtastr)
1075 doit op announcer 1079 doitL op announcer
1076 akey 1080 akey
1077 (AnnounceMethod qsearch (Left $ asend pub) 1081 (SearchMethod qsearch (asend pub)
1078 (\nid -> R.kclosest (searchSpace qsearch) 1082 (\nid -> R.kclosest (searchSpace qsearch)
1079 searchK 1083 searchK
1080 nid 1084 nid