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; }
static int s_snapshots (zloop_t *loop, zmq_pollitem_t *poller, void *args) { clonesrv_t *self = (clonesrv_t *) args; zframe_t *identity = zframe_recv (poller->socket); if (identity) { // Request is in second frame of message char *request = zstr_recv (poller->socket); char *subtree = NULL; if (streq (request, "ICANHAZ?")) { free (request); subtree = zstr_recv (poller->socket); } else printf ("E: bad request, aborting\n"); if (subtree) { // Send state socket to client kvroute_t routing = { poller->socket, identity, subtree }; zhash_foreach (self->kvmap, s_send_single, &routing); // Now send END message with sequence number zclock_log ("I: sending shapshot=%d", (int) self->sequence); zframe_send (&identity, poller->socket, ZFRAME_MORE); kvmsg_t *kvmsg = kvmsg_new (self->sequence); kvmsg_set_key (kvmsg, "KTHXBAI"); kvmsg_set_body (kvmsg, (byte *) subtree, 0); kvmsg_send (kvmsg, poller->socket); kvmsg_destroy (&kvmsg); free (subtree); } } return 0; }
int main (void) { // Prepare our context and sockets zctx_t *ctx = zctx_new (); void *publisher = zsocket_new (ctx, ZMQ_PUB); zsocket_bind (publisher, "tcp://*:5557"); int64_t sequence = 0; srandom ((unsigned) time (NULL)); // Start state manager and wait for synchronization signal void *updates = zthread_fork (ctx, state_manager, NULL); free (zstr_recv (updates)); while (!zctx_interrupted) { // Distribute as key-value message kvmsg_t *kvmsg = kvmsg_new (++sequence); kvmsg_fmt_key (kvmsg, "%d", randof (10000)); kvmsg_fmt_body (kvmsg, "%d", randof (1000000)); kvmsg_send (kvmsg, publisher); kvmsg_send (kvmsg, updates); kvmsg_destroy (&kvmsg); } printf (" Interrupted\n%d messages out\n", (int) sequence); zctx_destroy (&ctx); return 0; }
static int s_collector (zloop_t *loop, zmq_pollitem_t *poller, void *args) { clonesrv_t *self = (clonesrv_t *) args; kvmsg_t *kvmsg = kvmsg_recv (poller->socket); if (kvmsg) { if (self->active) { kvmsg_set_sequence (kvmsg, ++self->sequence); kvmsg_send (kvmsg, self->publisher); int ttl = atoi (kvmsg_get_prop (kvmsg, "ttl")); if (ttl) kvmsg_set_prop (kvmsg, "ttl", "%" PRId64, zclock_time () + ttl * 1000); kvmsg_store (&kvmsg, self->kvmap); zclock_log ("I: publishing update=%d", (int) self->sequence); } else { // If we already got message from active, drop it, else // hold on pending list if (s_was_pending (self, kvmsg)) kvmsg_destroy (&kvmsg); else zlist_append (self->pending, kvmsg); } } return 0; }
static void state_manager (void *args, zctx_t *ctx, void *pipe) { zhash_t *kvmap = zhash_new (); zstr_send (pipe, "READY"); void *snapshot = zsocket_new (ctx, ZMQ_ROUTER); zsocket_bind (snapshot, "tcp://*:5556"); zmq_pollitem_t items [] = { { pipe, 0, ZMQ_POLLIN, 0 }, { snapshot, 0, ZMQ_POLLIN, 0 } }; int64_t sequence = 0; // Current snapshot version number while (!zctx_interrupted) { int rc = zmq_poll (items, 2, -1); if (rc == -1 && errno == ETERM) break; // Context has been shut down // Apply state update from main thread if (items [0].revents & ZMQ_POLLIN) { kvmsg_t *kvmsg = kvmsg_recv (pipe); if (!kvmsg) break; // Interrupted sequence = kvmsg_sequence (kvmsg); kvmsg_store (&kvmsg, kvmap); } // Execute state snapshot request if (items [1].revents & ZMQ_POLLIN) { zframe_t *identity = zframe_recv (snapshot); if (!identity) break; // Interrupted // Request is in second frame of message char *request = zstr_recv (snapshot); if (streq (request, "ICANHAZ?")) free (request); else { printf ("E: bad request, aborting\n"); break; } // Send state snapshot to client kvroute_t routing = { snapshot, identity }; // For each entry in kvmap, send kvmsg to client zhash_foreach (kvmap, s_send_single, &routing); // Now send END message with sequence number printf ("Sending state shapshot=%d\n", (int) sequence); zframe_send (&identity, snapshot, ZFRAME_MORE); kvmsg_t *kvmsg = kvmsg_new (sequence); kvmsg_set_key (kvmsg, "KTHXBAI"); kvmsg_set_body (kvmsg, (byte *) "", 0); kvmsg_send (kvmsg, snapshot); kvmsg_destroy (&kvmsg); } } zhash_destroy (&kvmap); }
static void collabclient_sendRedo_sfdfragment( cloneclient_t* cc, SplineFont* sf, SplineChar *sc, /* optional */ char* sfdfrag, /* SFD that makes sense for redo */ char* msgtype, /* will be MSG_TYPE_UNDO if set to zero */ int isLocalUndo ) { printf("collabclient_sendRedo_sfdfragment(top)\n"); if( !cc ) return; if( !msgtype ) msgtype = MSG_TYPE_UNDO; char pos[100]; char* uuid = sf->xuid; printf("uuid:%s\n", uuid ); int idx = 0; char* sfd = sfdfrag; printf("read undo sfd, data:%p\n", sfd ); if( DEBUG_SHOW_SFD_CHUNKS ) printf("SENDING: %s\n\n", sfd ); printf("timers1...\n" ); cc->roundTripTimerWaitingSeq = cc->publisher_sendseq; BackgroundTimer_touch( cc->roundTripTimer ); printf("timers2...\n" ); printf("sfd:%p...\n", sfd ); kvmsg_t *kvmsg = kvmsg_new(0); kvmsg_fmt_key (kvmsg, "%s%d", SUBTREE, cc->publisher_sendseq++); kvmsg_set_body (kvmsg, sfd, strlen(sfd) ); kvmsg_set_prop (kvmsg, "type", msgtype ); kvmsg_set_prop (kvmsg, "uuid", uuid ); if( sc ) { sprintf(pos, "%d", sc->orig_pos ); printf("pos:%s\n", pos ); printf("sc.name:%s\n", sc->name ); } size_t data_size = kvmsg_size (kvmsg); printf("data.size:%ld\n", data_size ); kvmsg_set_prop (kvmsg, "pos", pos ); if( sc ) kvmsg_set_prop (kvmsg, "name", sc->name ); sprintf(pos, "%d", isLocalUndo ); kvmsg_set_prop (kvmsg, "isLocalUndo", pos ); kvmsg_send (kvmsg, cc->publisher); kvmsg_destroy (&kvmsg); DEBUG("Sent a undo chunk of %zu bytes to the server\n",strlen(sfd)); free(sfd); }
static void collabclient_sendRedo_Internal( FontViewBase *fv, SplineChar *sc, Undoes *undo, int isLocalUndo ) { printf("collabclient_sendRedo_Internal()\n"); cloneclient_t* cc = fv->collabClient; if( !cc ) return; char* uuid = fv->sf->xuid; printf("uuid:%s\n", uuid ); printf("________________________ WRITE undo.layer: %d layer_sz:%d\n", undo->layer, sc->layer_cnt ); int idx = 0; char filename[PATH_MAX]; snprintf(filename, PATH_MAX, "%s/fontforge-collab-x.sfd", getTempDir() ); FILE* f = fopen( filename, "wb" ); SFDDumpUndo( f, sc, undo, "Undo", idx ); fclose(f); printf("wrote undo sfd... filename: %s\n", filename ); char* sfd = GFileReadAll( filename ); printf("read undo sfd, data:%p\n", sfd ); if( DEBUG_SHOW_SFD_CHUNKS ) printf("SENDING: %s\n\n", sfd ); printf("timers1...\n" ); cc->roundTripTimerWaitingSeq = cc->publisher_sendseq; BackgroundTimer_touch( cc->roundTripTimer ); printf("timers2...\n" ); printf("sfd:%p...\n", sfd ); kvmsg_t *kvmsg = kvmsg_new(0); kvmsg_fmt_key (kvmsg, "%s%d", SUBTREE, cc->publisher_sendseq++); kvmsg_set_body (kvmsg, sfd, strlen(sfd) ); kvmsg_set_prop (kvmsg, "type", MSG_TYPE_UNDO ); kvmsg_set_prop (kvmsg, "uuid", uuid ); char pos[100]; sprintf(pos, "%d", sc->orig_pos ); printf("pos:%s\n", pos ); printf("sc.name:%s\n", sc->name ); size_t data_size = kvmsg_size (kvmsg); printf("data.size:%ld\n", data_size ); kvmsg_set_prop (kvmsg, "pos", pos ); kvmsg_set_prop (kvmsg, "name", sc->name ); sprintf(pos, "%d", isLocalUndo ); kvmsg_set_prop (kvmsg, "isLocalUndo", pos ); kvmsg_send (kvmsg, cc->publisher); kvmsg_destroy (&kvmsg); DEBUG("Sent a undo chunk of %d bytes to the server\n",strlen(sfd)); free(sfd); }
static int s_send_hugz (zloop_t *loop, int timer_id, void *args) { clonesrv_t *self = (clonesrv_t *) args; kvmsg_t *kvmsg = kvmsg_new (self->sequence); kvmsg_set_key (kvmsg, "HUGZ"); kvmsg_set_body (kvmsg, (byte *) "", 0); kvmsg_send (kvmsg, self->publisher); kvmsg_destroy (&kvmsg); return 0; }
static void collabclient_sendSFD( void* ccvp, char* sfd, char* fontname ) { cloneclient_t* cc = (cloneclient_t*)ccvp; kvmsg_t *kvmsg = kvmsg_new(0); kvmsg_fmt_key (kvmsg, "%s%d", SUBTREE, cc->publisher_sendseq++); kvmsg_set_body (kvmsg, sfd, strlen(sfd)); kvmsg_set_prop (kvmsg, "type", MSG_TYPE_SFD ); kvmsg_set_prop (kvmsg, "fontname", fontname ); // kvmsg_set_prop (kvmsg, "ttl", "%d", randof (30)); kvmsg_send (kvmsg, cc->publisher); kvmsg_destroy (&kvmsg); DEBUG("Sent a SFD of %d bytes to the server\n",strlen(sfd)); free(sfd); }
/** * A callback function that should be invoked when there is input in * the zeromq fd zeromq_fd. The collabclient is taken as the user data * pointer. * * Add this callback using GDrawAddReadFD() */ static void zeromq_subscriber_fd_callback(int zeromq_fd, void* datas ) { cloneclient_t* cc = (cloneclient_t*)datas; // printf("zeromq_subscriber_fd_callback(1)\n"); int opt = 0; size_t optsz = sizeof(int); zmq_getsockopt( cc->subscriber, ZMQ_EVENTS, &opt, &optsz ); if( opt & ZMQ_POLLIN ) { printf("zeromq_subscriber_fd_callback() have message! cc:%p\n",cc); while( 1 ) { kvmsg_t *kvmsg = kvmsg_recv_full (cc->subscriber, ZMQ_DONTWAIT); if (kvmsg) { int64_t msgseq = kvmsg_sequence (kvmsg); // Discard out-of-sequence kvmsgs, incl. heartbeats if ( msgseq > cc->sequence) { int create = 1; zeromq_subscriber_process_update( cc, kvmsg, create ); kvmsg_store (&kvmsg, cc->kvmap); printf ("I: received update=%d\n", (int) cc->sequence); } else { kvmsg_destroy (&kvmsg); } } else break; } } }
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; }
int main (int argc, char *argv []) { clonesrv_t *self = (clonesrv_t *) zmalloc (sizeof (clonesrv_t)); if (argc == 2 && streq (argv [1], "-p")) { zclock_log ("I: primary active, waiting for backup (passive)"); self->bstar = bstar_new (BSTAR_PRIMARY, "tcp://*:5003", "tcp://localhost:5004"); bstar_voter (self->bstar, "tcp://*:5556", ZMQ_ROUTER, s_snapshots, self); self->port = 5556; self->peer = 5566; self->primary = true; } else if (argc == 2 && streq (argv [1], "-b")) { zclock_log ("I: backup passive, waiting for primary (active)"); self->bstar = bstar_new (BSTAR_BACKUP, "tcp://*:5004", "tcp://localhost:5003"); bstar_voter (self->bstar, "tcp://*:5566", ZMQ_ROUTER, s_snapshots, self); self->port = 5566; self->peer = 5556; self->primary = false; } else { printf ("Usage: clonesrv4 { -p | -b }\n"); free (self); exit (0); } // Primary server will become first active if (self->primary) self->kvmap = zhash_new (); self->ctx = zctx_new (); self->pending = zlist_new (); bstar_set_verbose (self->bstar, true); // Set up our clone server sockets self->publisher = zsocket_new (self->ctx, ZMQ_PUB); self->collector = zsocket_new (self->ctx, ZMQ_SUB); zsocket_set_subscribe (self->collector, ""); zsocket_bind (self->publisher, "tcp://*:%d", self->port + 1); zsocket_bind (self->collector, "tcp://*:%d", self->port + 2); // Set up our own clone client interface to peer self->subscriber = zsocket_new (self->ctx, ZMQ_SUB); zsocket_set_subscribe (self->subscriber, ""); zsocket_connect (self->subscriber, "tcp://localhost:%d", self->peer + 1); // .split main task body // After we've setup our sockets, we register our binary star // event handlers, and then start the bstar reactor. This finishes // when the user presses Ctrl-C or when the process receives a SIGINT // interrupt: // Register state change handlers bstar_new_active (self->bstar, s_new_active, self); bstar_new_passive (self->bstar, s_new_passive, self); // Register our other handlers with the bstar reactor zmq_pollitem_t poller = { self->collector, 0, ZMQ_POLLIN }; zloop_poller (bstar_zloop (self->bstar), &poller, s_collector, self); zloop_timer (bstar_zloop (self->bstar), 1000, 0, s_flush_ttl, self); zloop_timer (bstar_zloop (self->bstar), 1000, 0, s_send_hugz, self); // Start the bstar reactor bstar_start (self->bstar); // Interrupted, so shut down while (zlist_size (self->pending)) { kvmsg_t *kvmsg = (kvmsg_t *) zlist_pop (self->pending); kvmsg_destroy (&kvmsg); } zlist_destroy (&self->pending); bstar_destroy (&self->bstar); zhash_destroy (&self->kvmap); zctx_destroy (&self->ctx); free (self); return 0; }
int main (void) { // Prepare our context and sockets zctx_t *ctx = zctx_new (); void *snapshot = zsocket_new (ctx, ZMQ_ROUTER); zsocket_bind (snapshot, "tcp://*:5556"); void *publisher = zsocket_new (ctx, ZMQ_PUB); zsocket_bind (publisher, "tcp://*:5557"); void *collector = zsocket_new (ctx, ZMQ_PULL); zsocket_bind (collector, "tcp://*:5558"); // .split body of main task // The body of the main task collects updates from clients and // publishes them back out to clients: int64_t sequence = 0; zhash_t *kvmap = zhash_new (); zmq_pollitem_t items [] = { { collector, 0, ZMQ_POLLIN, 0 }, { snapshot, 0, ZMQ_POLLIN, 0 } }; while (!zctx_interrupted) { int rc = zmq_poll (items, 2, 1000 * ZMQ_POLL_MSEC); // Apply state update sent from client if (items [0].revents & ZMQ_POLLIN) { kvmsg_t *kvmsg = kvmsg_recv (collector); if (!kvmsg) break; // Interrupted kvmsg_set_sequence (kvmsg, ++sequence); kvmsg_send (kvmsg, publisher); kvmsg_store (&kvmsg, kvmap); printf ("I: publishing update %5d\n", (int) sequence); } // Execute state snapshot request if (items [1].revents & ZMQ_POLLIN) { zframe_t *identity = zframe_recv (snapshot); if (!identity) break; // Interrupted // Request is in second frame of message char *request = zstr_recv (snapshot); if (streq (request, "ICANHAZ?")) free (request); else { printf ("E: bad request, aborting\n"); break; } // Send state snapshot to client kvroute_t routing = { snapshot, identity }; // For each entry in kvmap, send kvmsg to client zhash_foreach (kvmap, s_send_single, &routing); // Now send END message with sequence number printf ("I: sending shapshot=%d\n", (int) sequence); zframe_send (&identity, snapshot, ZFRAME_MORE); kvmsg_t *kvmsg = kvmsg_new (sequence); kvmsg_set_key (kvmsg, "KTHXBAI"); kvmsg_set_body (kvmsg, (byte *) "", 0); kvmsg_send (kvmsg, snapshot); kvmsg_destroy (&kvmsg); } } printf (" Interrupted\n%d messages handled\n", (int) sequence); zhash_destroy (&kvmap); zctx_destroy (&ctx); 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; }
int main (int argc, char *argv []) { clonesrv_t *self = (clonesrv_t *) zmalloc (sizeof (clonesrv_t)); if (argc == 2 && streq (argv [1], "-p")) { zclock_log ("I: primary master, waiting for backup (slave)"); self->bstar = bstar_new (BSTAR_PRIMARY, "tcp://*:5003", "tcp://localhost:5004"); bstar_voter (self->bstar, "tcp://*:5556", ZMQ_ROUTER, s_snapshots, self); self->port = 5556; self->peer = 5566; self->primary = TRUE; } else if (argc == 2 && streq (argv [1], "-b")) { zclock_log ("I: backup slave, waiting for primary (master)"); self->bstar = bstar_new (BSTAR_BACKUP, "tcp://*:5004", "tcp://localhost:5003"); bstar_voter (self->bstar, "tcp://*:5566", ZMQ_ROUTER, s_snapshots, self); self->port = 5566; self->peer = 5556; self->primary = FALSE; } else { printf ("Usage: clonesrv4 { -p | -b }\n"); free (self); exit (0); } // Primary server will become first master if (self->primary) self->kvmap = zhash_new (); self->ctx = zctx_new (); self->pending = zlist_new (); bstar_set_verbose (self->bstar, TRUE); // Set up our clone server sockets self->publisher = zsocket_new (self->ctx, ZMQ_PUB); self->collector = zsocket_new (self->ctx, ZMQ_SUB); zsockopt_set_subscribe (self->collector, ""); zsocket_bind (self->publisher, "tcp://*:%d", self->port + 1); zsocket_bind (self->collector, "tcp://*:%d", self->port + 2); // Set up our own clone client interface to peer self->subscriber = zsocket_new (self->ctx, ZMQ_SUB); zsockopt_set_subscribe (self->subscriber, ""); zsocket_connect (self->subscriber, "tcp://localhost:%d", self->peer + 1); // Register state change handlers bstar_new_master (self->bstar, s_new_master, self); bstar_new_slave (self->bstar, s_new_slave, self); // Register our other handlers with the bstar reactor zmq_pollitem_t poller = { self->collector, 0, ZMQ_POLLIN }; zloop_poller (bstar_zloop (self->bstar), &poller, s_collector, self); zloop_timer (bstar_zloop (self->bstar), 1000, 0, s_flush_ttl, self); zloop_timer (bstar_zloop (self->bstar), 1000, 0, s_send_hugz, self); // Start the Bstar reactor bstar_start (self->bstar); // Interrupted, so shut down while (zlist_size (self->pending)) { kvmsg_t *kvmsg = (kvmsg_t *) zlist_pop (self->pending); kvmsg_destroy (&kvmsg); } zlist_destroy (&self->pending); bstar_destroy (&self->bstar); zhash_destroy (&self->kvmap); zctx_destroy (&self->ctx); free (self); return 0; }