static int s_subscriber (zloop_t *loop, zmq_pollitem_t *poller, void *args) { clonesrv_t *self = (clonesrv_t *) args; // Get state snapshot if necessary if (self->kvmap == NULL) { self->kvmap = zhash_new (); void *snapshot = zsocket_new (self->ctx, ZMQ_DEALER); zsocket_connect (snapshot, "tcp://localhost:%d", self->peer); zclock_log ("I: asking for snapshot from: tcp://localhost:%d", self->peer); zstr_sendm (snapshot, "ICANHAZ?"); zstr_send (snapshot, ""); // blank subtree to get all while (true) { kvmsg_t *kvmsg = kvmsg_recv (snapshot); if (!kvmsg) break; // Interrupted if (streq (kvmsg_key (kvmsg), "KTHXBAI")) { self->sequence = kvmsg_sequence (kvmsg); kvmsg_destroy (&kvmsg); break; // Done } kvmsg_store (&kvmsg, self->kvmap); } zclock_log ("I: received snapshot=%d", (int) self->sequence); zsocket_destroy (self->ctx, snapshot); } // Find and remove update off pending list kvmsg_t *kvmsg = kvmsg_recv (poller->socket); if (!kvmsg) return 0; if (strneq (kvmsg_key (kvmsg), "HUGZ")) { if (!s_was_pending (self, kvmsg)) { // If active update came before client update, flip it // around, store active update (with sequence) on pending // list and use to clear client update when it comes later zlist_append (self->pending, kvmsg_dup (kvmsg)); } // If update is more recent than our kvmap, apply it if (kvmsg_sequence (kvmsg) > self->sequence) { self->sequence = kvmsg_sequence (kvmsg); kvmsg_store (&kvmsg, self->kvmap); zclock_log ("I: received update=%d", (int) self->sequence); } else kvmsg_destroy (&kvmsg); } else kvmsg_destroy (&kvmsg); return 0; }
// Send one state snapshot key-value pair to a socket // Hash item data is our kvmsg object, ready to send static int s_send_single (const char *key, void *data, void *args) { kvroute_t *kvroute = (kvroute_t *) args; kvmsg_t *kvmsg = (kvmsg_t *) data; if (strlen (kvroute->subtree) <= strlen (kvmsg_key (kvmsg)) && memcmp (kvroute->subtree, kvmsg_key (kvmsg), strlen (kvroute->subtree)) == 0) { zframe_send (&kvroute->identity, // Choose recipient kvroute->socket, ZFRAME_MORE + ZFRAME_REUSE); kvmsg_send (kvmsg, kvroute->socket); } return 0; }
/** * We call this function for each key-value pair in our hash table */ static int s_send_single (const char *key, void *data, void *args) { kvroute_t *kvroute = (kvroute_t *) args; kvmsg_t *kvmsg = (kvmsg_t *) data; DEBUG ("I: s_send_single %"PRId64" type:%s", kvmsg_sequence(kvmsg), kvmsg_get_prop (kvmsg, "type") ); if (strlen (kvroute->subtree) <= strlen (kvmsg_key (kvmsg)) && memcmp (kvroute->subtree, kvmsg_key (kvmsg), strlen (kvroute->subtree)) == 0) { zframe_send (&kvroute->identity, // Choose recipient kvroute->socket, ZFRAME_MORE + ZFRAME_REUSE); kvmsg_send (kvmsg, kvroute->socket); } return 0; }
int main (void) { // Prepare our context and subscriber zctx_t *ctx = zctx_new (); void *snapshot = zsocket_new (ctx, ZMQ_DEALER); zsocket_connect (snapshot, "tcp://localhost:5556"); void *subscriber = zsocket_new (ctx, ZMQ_SUB); zsockopt_set_subscribe (subscriber, ""); zsocket_connect (subscriber, "tcp://localhost:5557"); void *publisher = zsocket_new (ctx, ZMQ_PUSH); zsocket_connect (publisher, "tcp://localhost:5558"); zhash_t *kvmap = zhash_new (); srandom ((unsigned) time (NULL)); // Get state snapshot int64_t sequence = 0; zstr_send (snapshot, "ICANHAZ?"); while (TRUE) { kvmsg_t *kvmsg = kvmsg_recv (snapshot); if (!kvmsg) break; // Interrupted if (streq (kvmsg_key (kvmsg), "KTHXBAI")) { sequence = kvmsg_sequence (kvmsg); printf ("I: received snapshot=%d\n", (int) sequence); kvmsg_destroy (&kvmsg); break; // Done } kvmsg_store (&kvmsg, kvmap); } int64_t alarm = zclock_time () + 1000; while (!zctx_interrupted) { zmq_pollitem_t items [] = { { subscriber, 0, ZMQ_POLLIN, 0 } }; int tickless = (int) ((alarm - zclock_time ())); if (tickless < 0) tickless = 0; int rc = zmq_poll (items, 1, tickless * ZMQ_POLL_MSEC); if (rc == -1) break; // Context has been shut down if (items [0].revents & ZMQ_POLLIN) { kvmsg_t *kvmsg = kvmsg_recv (subscriber); if (!kvmsg) break; // Interrupted // Discard out-of-sequence kvmsgs, incl. heartbeats if (kvmsg_sequence (kvmsg) > sequence) { sequence = kvmsg_sequence (kvmsg); kvmsg_store (&kvmsg, kvmap); printf ("I: received update=%d\n", (int) sequence); } else kvmsg_destroy (&kvmsg); } // If we timed-out, generate a random kvmsg if (zclock_time () >= alarm) { kvmsg_t *kvmsg = kvmsg_new (0); kvmsg_fmt_key (kvmsg, "%d", randof (10000)); kvmsg_fmt_body (kvmsg, "%d", randof (1000000)); kvmsg_send (kvmsg, publisher); kvmsg_destroy (&kvmsg); alarm = zclock_time () + 1000; } } printf (" Interrupted\n%d messages in\n", (int) sequence); zhash_destroy (&kvmap); zctx_destroy (&ctx); return 0; }
int main (void) { // Prepare our context and subscriber void *context = zmq_init (1); void *subscriber = zmq_socket (context, ZMQ_SUB); zmq_setsockopt (subscriber, ZMQ_SUBSCRIBE, "", 0); zmq_connect (subscriber, "tcp://localhost:5556"); void *snapshot = zmq_socket (context, ZMQ_XREQ); zmq_connect (snapshot, "tcp://localhost:5557"); void *updates = zmq_socket (context, ZMQ_PUSH); zmq_connect (updates, "tcp://localhost:5558"); s_catch_signals (); zhash_t *kvmap = zhash_new (); srandom ((unsigned) time (NULL)); // Get state snapshot int64_t sequence = 0; s_send (snapshot, "I can haz state?"); while (!s_interrupted) { kvmsg_t *kvmsg = kvmsg_recv (snapshot); if (!kvmsg) break; // Interrupted if (streq (kvmsg_key (kvmsg), "KTHXBAI")) { sequence = kvmsg_sequence (kvmsg); kvmsg_destroy (&kvmsg); break; // Done } kvmsg_store (&kvmsg, kvmap); } printf ("I: received snapshot=%" PRId64 "\n", sequence); int zero = 0; zmq_setsockopt (snapshot, ZMQ_LINGER, &zero, sizeof (zero)); zmq_close (snapshot); int64_t alarm = s_clock () + 1000; while (!s_interrupted) { zmq_pollitem_t items [] = { { subscriber, 0, ZMQ_POLLIN, 0 } }; int tickless = (int) ((alarm - s_clock ())); if (tickless < 0) tickless = 0; int rc = zmq_poll (items, 1, tickless * 1000); if (rc == -1) break; // Context has been shut down if (items [0].revents & ZMQ_POLLIN) { kvmsg_t *kvmsg = kvmsg_recv (subscriber); if (!kvmsg) break; // Interrupted // Discard out-of-sequence kvmsgs, incl. heartbeats if (kvmsg_sequence (kvmsg) > sequence) { sequence = kvmsg_sequence (kvmsg); kvmsg_store (&kvmsg, kvmap); printf ("I: received update=%" PRId64 "\n", sequence); } else kvmsg_destroy (&kvmsg); } // If we timed-out, generate a random kvmsg if (s_clock () >= alarm) { kvmsg_t *kvmsg = kvmsg_new (0); kvmsg_fmt_key (kvmsg, "%d", randof (10000)); kvmsg_fmt_body (kvmsg, "%d", randof (1000000)); kvmsg_send (kvmsg, updates); kvmsg_destroy (&kvmsg); alarm = s_clock () + 1000; } } zhash_destroy (&kvmap); printf (" Interrupted\n%" PRId64 " messages in\n", sequence); zmq_setsockopt (updates, ZMQ_LINGER, &zero, sizeof (zero)); zmq_close (updates); zmq_close (subscriber); zmq_term (context); return 0; }
FontViewBase* collabclient_sessionJoin( void* ccvp, FontView *fv ) { FontViewBase* ret = 0; #ifdef BUILD_COLLAB cloneclient_t* cc = (cloneclient_t*)ccvp; printf("collabclient_sessionJoin(top)\n"); // Get state snapshot cc->sequence = 0; zstr_sendm (cc->snapshot, "ICANHAZ?"); zstr_send (cc->snapshot, SUBTREE); // if we wait for timeoutMS millisec then we assume failure // timeWaitedMS is used to keep track of how long we have waited kvmsg_t* lastSFD = 0; int timeWaitedMS = 0; int timeoutMS = pref_collab_sessionJoinTimeoutMS; while (true) { printf("timeoutMS:%d timeWaitedMS:%d\n", timeoutMS, timeWaitedMS ); if( timeWaitedMS >= timeoutMS ) { char* addrport = collabclient_makeAddressString( cc->address, cc->port ); gwwv_post_error(_("FontForge Collaboration"), _("Failed to connect to server session at %s"), addrport ); return 0; } kvmsg_t *kvmsg = kvmsg_recv_full( cc->snapshot, ZMQ_DONTWAIT ); if (!kvmsg) { int msToWait = 50; g_usleep( msToWait * 1000 ); timeWaitedMS += msToWait; continue; } /* kvmsg_t *kvmsg = kvmsg_recv (cc->snapshot); */ /* if (!kvmsg) */ /* break; // Interrupted */ if (streq (kvmsg_key (kvmsg), "KTHXBAI")) { cc->sequence = kvmsg_sequence (kvmsg); printf ("I: received snapshot=%d\n", (int) cc->sequence); kvmsg_destroy (&kvmsg); // Done break; } printf ("I: storing seq=%ld ", kvmsg_sequence (kvmsg)); if( kvmsg_get_prop (kvmsg, "type") ) printf(" type:%s", kvmsg_get_prop (kvmsg, "type") ); printf ("\n"); if( !strcmp(kvmsg_get_prop (kvmsg, "type"), MSG_TYPE_SFD )) { if( !lastSFD || kvmsg_sequence (kvmsg) > kvmsg_sequence (lastSFD)) { lastSFD = kvmsg; } size_t data_size = kvmsg_size(lastSFD); printf("data_size:%ld\n", data_size ); } kvmsg_store (&kvmsg, cc->kvmap); } printf("collabclient_sessionJoin(done with netio getting snapshot)\n"); printf("collabclient_sessionJoin() lastSFD:%p\n", lastSFD ); // int rc = 0; // void* out = 0; // rc = zhash_foreach ( cc->kvmap, collabclient_sessionJoin_findSFD_foreach_fn, &out ); // printf("collabclient_sessionJoin() last sfd:%p\n", out ); if( !lastSFD ) { gwwv_post_error(_("FontForge Collaboration"), _("No Font Snapshot received from the server")); return 0; } if( lastSFD ) { int openflags = 0; char filename[PATH_MAX]; snprintf(filename, PATH_MAX, "%s/fontforge-collab-import-%d.sfd",getTempDir(),getpid()); GFileWriteAll( filename, kvmsg_body (lastSFD) ); /* * Since we are creating a new fontview window for the collab session * we "hand over" the collabClient from the current fontview used to join * the session to the fontview which owns the sfd from the wire. */ FontViewBase* newfv = ViewPostScriptFont( filename, openflags ); newfv->collabClient = cc; fv->b.collabClient = 0; newfv->collabState = cs_client; FVTitleUpdate( newfv ); collabclient_notifySessionJoining( cc, newfv ); ret = newfv; /* cloneclient_t* newc = collabclient_new( cc->address, cc->port ); */ /* collabclient_sessionJoin( newc, fv ); */ } printf("applying updates from server that were performed after the SFD snapshot was done...\n"); kvmap_visit( cc->kvmap, kvmsg_sequence (lastSFD), collabclient_sessionJoin_processmsg_foreach_fn, cc ); /* struct collabclient_sessionJoin_processUpdates_foreach_args args; */ /* args.cc = cc; */ /* args.lastSFD = lastSFD; */ /* rc = zhash_foreach ( cc->kvmap, collabclient_sessionJoin_processUpdates_foreach_fn, &args ); */ printf("collabclient_sessionJoin(complete)\n"); #endif return ret; }