static void * fq_conn_worker(void *u) { int backoff = 0, i; bool zero; fq_conn_s *conn_s = (fq_conn_s *)u; cmd_instr *last_entries[MAX_PENDING] = { NULL }; int last_entry_idx = 0; int next_entry_idx = 0; #define SAVE_ENTRY(e) do { \ if(last_entries[next_entry_idx] != NULL) { \ CONNERR(conn_s, "exceed max cmd pipeline"); \ goto restart; \ } \ last_entries[next_entry_idx++] = e; \ next_entry_idx = next_entry_idx % MAX_PENDING; \ } while(0) #define last_entry last_entries[last_entry_idx] #define PROCESS_ENTRY(e, should_free) do { \ fq_assert(last_entries[last_entry_idx] == e); \ if(should_free) free(last_entries[last_entry_idx]); \ last_entries[last_entry_idx++] = NULL; \ last_entry_idx = last_entry_idx % MAX_PENDING; \ } while(0) cmd_instr *entry; ck_pr_inc_uint(&conn_s->thrcnt); while(conn_s->stop == 0) { int wait_ms = 50; if(fq_client_connect_internal(conn_s) == 0) { backoff = 0; /* we're good, restart our backoff */ } while(conn_s->data_ready && conn_s->stop == 0) { hrtime_t t; unsigned long long hb_us; struct timeval tv; int rv; ck_fifo_spsc_dequeue_lock(&conn_s->cmdq); while(ck_fifo_spsc_dequeue(&conn_s->cmdq, &entry) == true) { #ifdef DEBUG fq_debug(FQ_DEBUG_CONN, "client acting on user req 0x%04x\n", entry->cmd); #endif switch(entry->cmd) { case FQ_PROTO_STATUSREQ: fq_debug(FQ_DEBUG_CONN, "sending status request\n"); if(fq_write_uint16(conn_s->cmd_fd, entry->cmd)) { free(entry); CONNERR(conn_s, "write failed (statusreq)"); goto restart; } SAVE_ENTRY(entry); break; case FQ_PROTO_HBREQ: fq_debug(FQ_DEBUG_CONN, "sending heartbeat request\n"); if(fq_write_uint16(conn_s->cmd_fd, entry->cmd) || fq_write_uint16(conn_s->cmd_fd, entry->data.heartbeat.ms)) { free(entry); CONNERR(conn_s, "write failed (hbreq)"); goto restart; } conn_s->cmd_hb_ms = entry->data.heartbeat.ms; tv.tv_sec = (unsigned long)entry->data.heartbeat.ms / 1000; tv.tv_usec = 1000UL * (entry->data.heartbeat.ms % 1000); if(setsockopt(conn_s->cmd_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) CONNERR(conn_s, strerror(errno)); tv.tv_sec = (unsigned long)entry->data.heartbeat.ms / 1000; tv.tv_usec = 1000UL * (entry->data.heartbeat.ms % 1000); if(setsockopt(conn_s->cmd_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv))) CONNERR(conn_s, strerror(errno)); conn_s->cmd_hb_last = fq_gethrtime(); free(entry); break; case FQ_PROTO_BINDREQ: { unsigned short flags = entry->data.bind->flags; if(fq_write_uint16(conn_s->cmd_fd, entry->cmd) || fq_write_uint16(conn_s->cmd_fd, flags) || fq_write_short_cmd(conn_s->cmd_fd, entry->data.bind->exchange.len, entry->data.bind->exchange.name) < 0 || fq_write_short_cmd(conn_s->cmd_fd, strlen(entry->data.bind->program), entry->data.bind->program) < 0) { CONNERR(conn_s, "write failed (bindreq)"); goto restart; } SAVE_ENTRY(entry); } break; case FQ_PROTO_UNBINDREQ: { if(fq_write_uint16(conn_s->cmd_fd, entry->cmd) || fq_write_uint32(conn_s->cmd_fd, entry->data.unbind->route_id) || fq_write_short_cmd(conn_s->cmd_fd, entry->data.unbind->exchange.len, entry->data.unbind->exchange.name) < 0) { CONNERR(conn_s, "write failed (unbindreq)"); goto restart; } SAVE_ENTRY(entry); } break; default: CONNERR(conn_s, "unknown user-side cmd"); free(entry); } } ck_fifo_spsc_dequeue_unlock(&conn_s->cmdq); if(conn_s->cmd_hb_needed) { #ifdef DEBUG fq_debug(FQ_DEBUG_CONN, "-> heartbeat\n"); #endif if(fq_write_uint16(conn_s->cmd_fd, FQ_PROTO_HB)) { CONNERR(conn_s, "write failed (hb)"); break; } conn_s->cmd_hb_needed = 0; } rv = fq_client_wfrw_internal(conn_s->cmd_fd, 1, 0, wait_ms, NULL); if(rv == 0) { wait_ms = (wait_ms >> 2) + wait_ms; if(conn_s->cmd_hb_ms && wait_ms > conn_s->cmd_hb_ms) wait_ms = conn_s->cmd_hb_ms; if(wait_ms > 1000) wait_ms = 1000; } else wait_ms = 50; fq_debug(FQ_DEBUG_CONN, "fq_client_wfrw_internal(cmd:%d) -> %d\n", conn_s->cmd_fd, rv); t = fq_gethrtime(); hb_us = (unsigned long long)conn_s->cmd_hb_ms * 3 * 1000000ULL; if(conn_s->cmd_hb_last && hb_us && conn_s->cmd_hb_last < (unsigned int) (t - hb_us)) { char errbuf[256]; snprintf(errbuf, sizeof(errbuf), "heartbeat failed [%llu - %llu = %llu]", (unsigned long long)t, (unsigned long long)conn_s->cmd_hb_last, (unsigned long long)(t - conn_s->cmd_hb_last)); CONNERR(conn_s, errbuf); break; } if(rv < 0) { CONNERR(conn_s, strerror(errno)); break; } if(rv > 0) { bool handled = false; uint16_t hb; if(fq_read_uint16(conn_s->cmd_fd, &hb)) break; switch(hb) { case FQ_PROTO_HB: #ifdef DEBUG fq_debug(FQ_DEBUG_CONN, "<- heartbeat\n"); #endif conn_s->cmd_hb_last = fq_gethrtime(); conn_s->cmd_hb_needed = 1; break; case FQ_PROTO_STATUS: if(!last_entry || last_entry->cmd != FQ_PROTO_STATUSREQ) { CONNERR(conn_s, "protocol violation (status unexpected)"); goto restart; } if(fq_read_status(conn_s->cmd_fd, last_entry->data.status.callback, last_entry->data.status.closure)) goto restart; PROCESS_ENTRY(last_entry, 1); break; case FQ_PROTO_BIND: if(!last_entry || last_entry->cmd != FQ_PROTO_BINDREQ) { CONNERR(conn_s, "protocol violation (bind unexpected)"); goto restart; } if(fq_read_uint32(conn_s->cmd_fd, &last_entry->data.bind->out__route_id)) { if(conn_s->bind_hook) { if(conn_s->sync_hooks) { enqueue_cmd_hook_req(conn_s, last_entry); PROCESS_ENTRY(last_entry, 0); handled = true; } else conn_s->bind_hook((fq_client)conn_s, last_entry->data.bind); } CONNERR(conn_s, "read failed (bind)"); goto restart; } if(conn_s->bind_hook) { if(conn_s->sync_hooks) { enqueue_cmd_hook_req(conn_s, last_entry); PROCESS_ENTRY(last_entry, 0); handled = true; } else conn_s->bind_hook((fq_client)conn_s, last_entry->data.bind); } if(!handled) PROCESS_ENTRY(last_entry, 1); break; case FQ_PROTO_UNBIND: if(!last_entry || last_entry->cmd != FQ_PROTO_UNBINDREQ) { CONNERR(conn_s, "protocol violation (unbind unexpected)"); goto restart; } if(fq_read_uint32(conn_s->cmd_fd, &last_entry->data.unbind->out__success)) { if(conn_s->unbind_hook) { if(conn_s->sync_hooks) { enqueue_cmd_hook_req(conn_s, last_entry); PROCESS_ENTRY(last_entry, 0); handled = true; } conn_s->unbind_hook((fq_client)conn_s, last_entry->data.unbind); } CONNERR(conn_s, "read failed (unbind)"); goto restart; } if(conn_s->unbind_hook) { if(conn_s->sync_hooks) { enqueue_cmd_hook_req(conn_s, last_entry); PROCESS_ENTRY(last_entry, 0); handled = true; last_entry = NULL; } conn_s->unbind_hook((fq_client)conn_s, last_entry->data.unbind); } if(!handled) PROCESS_ENTRY(last_entry,1); break; default: CONNERR(conn_s, "protocol violation"); goto restart; break; } } }
static void * fq_conn_worker(void *u) { int backoff = 0; fq_conn_s *conn_s = (fq_conn_s *)u; cmd_instr *last_entry = NULL; uint16_t expect; while(conn_s->stop == 0) { restart: expect = 0; if(fq_client_connect_internal(conn_s) == 0) { backoff = 0; /* we're good, restart our backoff */ } while(conn_s->data_ready) { hrtime_t t; unsigned long long hb_us; struct timeval tv; int rv; cmd_instr *entry; ck_fifo_mpmc_entry_t *garbage; while(ck_fifo_mpmc_dequeue(&conn_s->cmdq, &entry, &garbage) == true) { free(garbage); #ifdef DEBUG fq_debug(FQ_DEBUG_CONN, "client acting on user req 0x%04x\n", entry->cmd); #endif switch(entry->cmd) { case FQ_PROTO_STATUSREQ: if(expect != 0) { if(conn_s->errorlog) conn_s->errorlog("protocol violation"); goto restart; } fq_debug(FQ_DEBUG_CONN, "sending status request\n"); if(fq_write_uint16(conn_s->cmd_fd, entry->cmd)) { free(entry); goto restart; } expect = FQ_PROTO_STATUS; last_entry = entry; break; case FQ_PROTO_HBREQ: fq_debug(FQ_DEBUG_CONN, "sending heartbeat request\n"); if(fq_write_uint16(conn_s->cmd_fd, entry->cmd) || fq_write_uint16(conn_s->cmd_fd, entry->data.heartbeat.ms)) { free(entry); goto restart; } conn_s->cmd_hb_ms = entry->data.heartbeat.ms; tv.tv_sec = (unsigned long)entry->data.heartbeat.ms / 1000; tv.tv_usec = 1000UL * (entry->data.heartbeat.ms % 1000); if(setsockopt(conn_s->cmd_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) CONNERR(conn_s, strerror(errno)); tv.tv_sec = (unsigned long)entry->data.heartbeat.ms / 1000; tv.tv_usec = 1000UL * (entry->data.heartbeat.ms % 1000); if(setsockopt(conn_s->cmd_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv))) CONNERR(conn_s, strerror(errno)); conn_s->cmd_hb_last = fq_gethrtime(); free(entry); break; case FQ_PROTO_BINDREQ: { unsigned short peermode = entry->data.bind->peermode ? 1 : 0; if(expect != 0) { if(conn_s->errorlog) conn_s->errorlog("protocol violation"); goto restart; } if(fq_write_uint16(conn_s->cmd_fd, entry->cmd) || fq_write_uint16(conn_s->cmd_fd, peermode) || fq_write_short_cmd(conn_s->cmd_fd, entry->data.bind->exchange.len, entry->data.bind->exchange.name) < 0 || fq_write_short_cmd(conn_s->cmd_fd, strlen(entry->data.bind->program), entry->data.bind->program) < 0) { goto restart; } expect = FQ_PROTO_BIND; last_entry = entry; } break; case FQ_PROTO_UNBINDREQ: { if(expect != 0) { if(conn_s->errorlog) conn_s->errorlog("protocol violation"); goto restart; } if(fq_write_uint16(conn_s->cmd_fd, entry->cmd) || fq_write_uint32(conn_s->cmd_fd, entry->data.unbind->route_id) || fq_write_short_cmd(conn_s->cmd_fd, entry->data.unbind->exchange.len, entry->data.unbind->exchange.name) < 0) { goto restart; } expect = FQ_PROTO_UNBIND; last_entry = entry; } break; default: if(conn_s->errorlog) conn_s->errorlog("unknown user-side cmd"); free(entry); } } if(conn_s->cmd_hb_needed) { #ifdef DEBUG fq_debug(FQ_DEBUG_CONN, "-> heartbeat\n"); #endif if(fq_write_uint16(conn_s->cmd_fd, FQ_PROTO_HB)) break; conn_s->cmd_hb_needed = 0; } rv = fq_client_wfrw_internal(conn_s->cmd_fd, 1, 0, 50, NULL); t = fq_gethrtime(); hb_us = (unsigned long long)conn_s->cmd_hb_ms * 3 * 1000000ULL; if(conn_s->cmd_hb_last && hb_us && conn_s->cmd_hb_last < (unsigned int) (t - hb_us)) { char errbuf[256]; snprintf(errbuf, sizeof(errbuf), "heartbeat failed [%llu - %llu = %llu]", (unsigned long long)t, (unsigned long long)conn_s->cmd_hb_last, (unsigned long long)(t - conn_s->cmd_hb_last)); CONNERR(conn_s, errbuf); break; } if(rv < 0) { CONNERR(conn_s, strerror(errno)); break; } if(rv > 0) { uint16_t hb; if(fq_read_uint16(conn_s->cmd_fd, &hb)) break; switch(hb) { case FQ_PROTO_HB: #ifdef DEBUG fq_debug(FQ_DEBUG_CONN, "<- heartbeat\n"); #endif conn_s->cmd_hb_last = fq_gethrtime(); conn_s->cmd_hb_needed = 1; break; case FQ_PROTO_STATUS: if(expect != FQ_PROTO_STATUS) { if(conn_s->errorlog) conn_s->errorlog("protocol violation"); goto restart; } if(fq_read_status(conn_s->cmd_fd, last_entry->data.status.callback, last_entry->data.status.closure)) goto restart; expect = 0; break; case FQ_PROTO_BIND: if(expect != FQ_PROTO_BIND) { if(conn_s->errorlog) conn_s->errorlog("protocol violation"); goto restart; } if(fq_read_uint32(conn_s->cmd_fd, &last_entry->data.bind->out__route_id)) goto restart; expect = 0; break; case FQ_PROTO_UNBIND: if(expect != FQ_PROTO_UNBIND) { if(conn_s->errorlog) conn_s->errorlog("protocol violation"); goto restart; } if(fq_read_uint32(conn_s->cmd_fd, &last_entry->data.unbind->out__success)) goto restart; expect = 0; break; default: if(conn_s->errorlog) conn_s->errorlog("protocol violation"); goto restart; break; } } } #ifdef DEBUG fq_debug(FQ_DEBUG_CONN, "[cmd] connection failed: %s\n", conn_s->error); #endif usleep(backoff); backoff += 10000; } fq_client_disconnect_internal(conn_s); return (void *)NULL; }