diff options
-rw-r--r-- | Announcer/Tox.hs | 102 | ||||
-rw-r--r-- | ToxManager.hs | 4 | ||||
-rw-r--r-- | ToxToXMPP.hs | 6 | ||||
-rw-r--r-- | examples/dhtd.hs | 20 |
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. | ||
77 | data 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: |
85 | newAnnouncement :: STM (IO a) | 110 | newAnnouncement :: 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. |
118 | schedule :: Announcer -> AnnounceKey -> AnnounceMethod r -> r -> IO () | 143 | scheduleAnnounce :: Announcer -> AnnounceKey -> AnnounceMethod r -> r -> IO () |
119 | schedule announcer k AnnounceMethod{aSearch,aPublish,aNearestNodes,aTarget,aInterval} r = do | 144 | scheduleAnnounce 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. | ||
186 | scheduleSearch :: Announcer -> AnnounceKey -> SearchMethod r -> r -> IO () | ||
187 | scheduleSearch 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 |