fpmas 1.5
hard_sync_linker.h
Go to the documentation of this file.
1#ifndef FPMAS_HARD_SYNC_LINKER_H
2#define FPMAS_HARD_SYNC_LINKER_H
3
8#include <set>
9
10#include "fpmas/utils/macros.h"
11
15#include "server_pack.h"
16#include "termination.h"
17
18namespace fpmas { namespace synchro { namespace hard {
19 using api::Tag;
20 using api::Epoch;
22
23 namespace hard_link {
27 template<typename T>
28 class LinkServer : public api::LinkServer {
29 public:
42 private:
43 Epoch epoch = Epoch::EVEN;
44
47 IdMpi& id_mpi;
48 EdgeMpi& edge_mpi;
49 std::set<DistributedId> locked_unlink_edges;
50 std::set<DistributedId> locked_remove_nodes;
51
52 public:
64 IdMpi& id_mpi, EdgeMpi& edge_mpi) :
65 comm(comm), graph(graph),
66 id_mpi(id_mpi), edge_mpi(edge_mpi) {}
67
68 Epoch getEpoch() const override {return epoch;}
69 void setEpoch(api::Epoch epoch) override {this->epoch = epoch;}
70
71 void handleIncomingRequests() override;
72
73
74 void lockUnlink(DistributedId edge_id) override {
75 locked_unlink_edges.insert(edge_id);
76 }
77
78 bool isLockedUnlink(DistributedId edge_id) override {
79 return locked_unlink_edges.count(edge_id) > 0;
80 }
81
82 void unlockUnlink(DistributedId edge_id) override {
83 locked_unlink_edges.erase(edge_id);
84 }
85
86 void lockRemoveNode(DistributedId node_id) override {
87 locked_remove_nodes.insert(node_id);
88 }
89
90 bool isLockedRemoveNode(DistributedId node_id) override {
91 return locked_remove_nodes.count(node_id);
92 }
93
94 void unlockRemoveNode(DistributedId node_id) override {
95 locked_remove_nodes.erase(node_id);
96 }
97 };
98
99 template<typename T>
102 // Check read request
103 if(edge_mpi.Iprobe(MPI_ANY_SOURCE, epoch | Tag::LINK, status)) {
104 EdgeApi* edge = edge_mpi.recv(status.source, status.tag);
105 FPMAS_LOGD(this->comm.getRank(), "LINK_SERVER",
106 "receive link request from %i", status.source);
107 graph.importEdge(edge);
108 }
109 if(id_mpi.Iprobe(MPI_ANY_SOURCE, epoch | Tag::UNLINK, status)) {
110 DistributedId unlink_id = id_mpi.recv(status.source, status.tag);
111 FPMAS_LOGD(this->comm.getRank(), "LINK_SERVER",
112 "receive unlink request %s from %i",
113 FPMAS_C_STR(unlink_id), status.source);
114 if(
115 // The edge is not being unlinking by the local process
116 !isLockedUnlink(unlink_id)
117 // The edge has not been unlinked by an other UNLINK
118 // operation
119 && graph.getEdges().count(unlink_id) > 0) {
120 auto* edge = graph.getEdge(unlink_id);
121 // Source or target node is not being removed by the local
122 // process. In this case, the local process is responsible
123 // for all the required unlink operations, so the incoming
124 // request is ignored.
125 if(!(isLockedRemoveNode(edge->getSourceNode()->getId())
126 || isLockedRemoveNode(edge->getTargetNode()->getId()))) {
127 graph.erase(edge);
128 }
129 }
130 }
131 if(id_mpi.Iprobe(MPI_ANY_SOURCE, epoch | Tag::REMOVE_NODE, status)) {
132 DistributedId node_id = id_mpi.recv(status.source, status.tag);
133 FPMAS_LOGD(this->comm.getRank(), "LINK_SERVER",
134 "receive remove node request %s from %i",
135 FPMAS_C_STR(node_id), status.source);
136
137 // Initiates a removeNode operation from the local process,
138 // that will trigger all required UNLINK operations
139 graph.removeNode(graph.getNode(node_id));
140 }
141 }
142
146 template<typename T>
147 class LinkClient : public api::LinkClient<T> {
148 public:
165
166 private:
168 IdMpi& id_mpi;
169 EdgeMpi& edge_mpi;
170 ServerPackBase& server_pack;
171
172 public:
186 IdMpi& id_mpi, EdgeMpi& edge_mpi,
187 ServerPackBase& server_pack) :
188 comm(comm), id_mpi(id_mpi), edge_mpi(edge_mpi),
189 server_pack(server_pack) {}
190
191 void link(const EdgeApi*) override;
192 void unlink(const EdgeApi*) override;
193 void removeNode(const NodeApi*) override;
194 };
195
196 template<typename T>
197 void LinkClient<T>::link(const EdgeApi* edge) {
198 bool distant_src =
200 bool distant_tgt =
202
203 if(edge->getSourceNode()->location() != edge->getTargetNode()->location()) {
204 // The edge is DISTANT, so at least of the two nodes is
205 // DISTANT. In this case, if the two nodes are
206 // DISTANT, they don't have the same location, so two
207 // requests must be performed.
210 // Simultaneously initiate the two requests, that are potentially
211 // made to different procs
212 if(distant_src) {
213 edge_mpi.Issend(
214 const_cast<EdgeApi*>(edge), edge->getSourceNode()->location(),
215 server_pack.getEpoch() | Tag::LINK, req_src
216 );
217 }
218 if(distant_tgt) {
219 edge_mpi.Issend(
220 const_cast<EdgeApi*>(edge), edge->getTargetNode()->location(),
221 server_pack.getEpoch() | Tag::LINK, req_tgt
222 );
223 }
224 // Sequentially waits for each request : if req_tgt completes
225 // before req_src, the second waitSendRequest will immediatly return
226 // so there is no performance issue.
227 if(distant_src) {
228 server_pack.waitSendRequest(req_src);
229 }
230 if(distant_tgt) {
231 server_pack.waitSendRequest(req_tgt);
232 }
233 } else {
234 // The edge is DISTANT, and its source and target nodes
235 // locations are the same, so the two nodes are necessarily
236 // located on the same DISTANT proc : only need to perform
237 // one request
239 edge_mpi.Issend(
240 const_cast<EdgeApi*>(edge), edge->getSourceNode()->location(),
241 server_pack.getEpoch() | Tag::LINK, req
242 );
243 server_pack.waitSendRequest(req);
244 }
245 }
246
247 template<typename T>
248 void LinkClient<T>::unlink(const EdgeApi* edge) {
249
250 bool distant_src
252 bool distant_tgt
254
257 // Simultaneously initiate the two requests, that are potentially
258 // made to different procs
259 if(distant_src) {
260 this->id_mpi.Issend(
261 edge->getId(), edge->getSourceNode()->location(),
262 server_pack.getEpoch() | Tag::UNLINK, req_src
263 );
264 }
265 if(distant_tgt) {
266 this->id_mpi.Issend(
267 edge->getId(), edge->getTargetNode()->location(),
268 server_pack.getEpoch() | Tag::UNLINK, req_tgt
269 );
270 }
271 // Sequentially waits for each request : if req_tgt completes
272 // before req_src, the second waitSendRequest will immediatly return
273 // so there is no performance issue.
274 if(distant_src) {
275 server_pack.waitSendRequest(req_src);
276 }
277 if(distant_tgt) {
278 server_pack.waitSendRequest(req_tgt);
279 }
280 }
281
282 template<typename T>
285 this->id_mpi.Issend(
286 node->getId(), node->location(),
287 server_pack.getEpoch() | Tag::REMOVE_NODE, req
288 );
289
290 server_pack.waitSendRequest(req);
291 }
292
293
307 template<typename T>
309 public:
318
319 private:
320 std::vector<EdgeApi*> ghost_edges;
322 api::LinkClient<T>& link_client;
324 std::vector<fpmas::api::graph::DistributedNode<T>*> nodes_to_remove;
325
326 public:
329 nodes_to_remove.push_back(node);
330 }
331
342 api::LinkClient<T>& link_client,
344 graph(graph), link_client(link_client), server_pack(server_pack) {
345 }
346
359 void link(EdgeApi* edge) override {
360 if(edge->state() == LocationState::DISTANT) {
361 link_client.link(edge);
362
364 && edge->getTargetNode()->state() == LocationState::DISTANT) {
365 ghost_edges.push_back(const_cast<EdgeApi*>(edge));
366 }
367 }
368 };
369
379 void unlink(EdgeApi* edge) override {
380 // Prevents other processes to unlink the edge while the local
381 // process is unlinking it. In this case, incoming unlink requests
382 // have no effect. (see LinkServer::handleIncomingRequests())
383 server_pack.linkServer().lockUnlink(edge->getId());
384
385 if(edge->state() == LocationState::DISTANT) {
386 link_client.unlink(edge);
387 }
388
389 // Unlocks the unlink operation. The edge will actually be erased
390 // from the graph upon return, in the DistributedGraph::unlink()
391 // method.
392 server_pack.linkServer().unlockUnlink(edge->getId());
393 };
394
410 void removeNode(NodeApi* node) override {
411 if(node->state() == LocationState::DISTANT) {
412 link_client.removeNode(node);
413 } else {
414 server_pack.linkServer().lockRemoveNode(node->getId());
415 for(auto edge : node->getOutgoingEdges())
416 graph.unlink(edge);
417 for(auto edge : node->getIncomingEdges())
418 graph.unlink(edge);
419 server_pack.linkServer().unlockRemoveNode(node->getId());
420 }
422 }
423
447 void synchronize() override {
448 FPMAS_LOGI(graph.getMpiCommunicator().getRank(),
449 "HARD_SYNC_LINKER", "Synchronizing sync linker...", "");
450 for(auto edge : ghost_edges)
451 graph.erase(edge);
452 ghost_edges.clear();
453
454 server_pack.terminate();
455
456 for(auto node : nodes_to_remove) {
457 graph.erase(node);
458 }
459 nodes_to_remove.clear();
460
461 FPMAS_LOGI(graph.getMpiCommunicator().getRank(),
462 "HARD_SYNC_LINKER", "Synchronized.", "");
463 };
464 };
465 }
466
467 namespace ghost_link {
477 template<typename T>
479 private:
481 TerminationAlgorithm termination;
482 ServerPackBase& server_pack;
483
484 public:
498 ServerPackBase& server_pack) :
499 ghost::GhostSyncLinkerBase<T>(edge_mpi, id_mpi, graph),
500 color_mpi(graph.getMpiCommunicator()),
501 termination(graph.getMpiCommunicator(), color_mpi),
502 server_pack(server_pack) {
503 }
504
505
506 void synchronize() override {
507 /*
508 * As specified by the
509 * fpmas::api::graph::DistributedGraph::synchronize()
510 * method, when a graph synchronization is performed,
511 * SyncLinker::synchronize() is called before
512 * DataSync::synchronize().
513 *
514 * But, in the context of this component, DataSync
515 * operations are performed on the fly using point-to-point
516 * communications, so it is necessary to finish those
517 * operations before initializing the collective
518 * synchronize_links() communications.
519 */
520 termination.terminate(server_pack);
521 this->synchronize_links();
522 }
523 };
524
525 }
526}}}
527#endif
Definition: communication.h:251
Definition: communication.h:637
Definition: distributed_edge.h:91
virtual LocationState state() const =0
Definition: distributed_graph.h:169
virtual api::communication::MpiCommunicator & getMpiCommunicator()=0
virtual void unlink(DistributedEdge< T > *edge)=0
Definition: distributed_id.h:89
Definition: distributed_node.h:28
virtual LocationState state() const =0
virtual int location() const =0
virtual IdType getId() const =0
virtual NodeType * getSourceNode() const =0
virtual NodeType * getTargetNode() const =0
virtual void erase(NodeType *node)=0
virtual const std::vector< EdgeType * > getIncomingEdges() const =0
virtual IdType getId() const =0
virtual const std::vector< EdgeType * > getOutgoingEdges() const =0
Definition: ptr_wrapper.h:21
Definition: ghost_mode.h:214
void synchronize_links()
Definition: ghost_mode.h:290
GhostSyncLinkerBase(EdgeMpi &edge_mpi, IdMpi &id_mpi, api::graph::DistributedGraph< T > &graph)
Definition: ghost_mode.h:262
Definition: server_pack.h:39
void terminate()
Definition: server_pack.h:119
Definition: server_pack.h:257
LinkServer & linkServer() override
Definition: server_pack.h:285
Definition: termination.h:26
void terminate(api::Server &server) override
Definition: termination.cpp:15
Definition: hard_sync_mode.h:59
Definition: client_server.h:294
virtual void unlink(const fpmas::api::graph::DistributedEdge< T > *edge)=0
virtual void removeNode(const fpmas::api::graph::DistributedNode< T > *node)=0
virtual void link(const fpmas::api::graph::DistributedEdge< T > *edge)=0
Definition: client_server.h:379
Definition: client_server.h:208
#define FPMAS_C_STR(arg)
Definition: macros.h:24
LocationState
Definition: location_state.h:15
@ DISTANT
Definition: location_state.h:28
Epoch
Definition: enums.h:15
Tag
Definition: enums.h:23
Definition: fpmas.cpp:3
Definition: communication.h:162
Definition: communication.h:215
int tag
Definition: communication.h:236
int source
Definition: communication.h:232