static void deregister_the_client (client_t *self) { if (*self->address) zsys_info ("client address='%s' - de-registering", self->address); // Cancel all stream subscriptions stream_t *stream = (stream_t *) zlistx_detach (self->readers, NULL); while (stream) { zsock_send (stream->actor, "sp", "CANCEL", self); stream = (stream_t *) zlistx_detach (self->readers, NULL); } // Cancel all service offerings service_t *service = (service_t *) zhashx_first (self->server->services); while (service) { offer_t *offer = (offer_t *) zlistx_first (service->offers); while (offer) { if (offer->client == self) zlistx_delete (service->offers, zlistx_cursor (service->offers)); offer = (offer_t *) zlistx_next (service->offers); } service = (service_t *) zhashx_next (self->server->services); } if (*self->address) zhashx_delete (self->server->clients, self->address); mlm_proto_set_status_code (self->message, MLM_PROTO_SUCCESS); }
static void deregister_the_client (client_t *self) { // If the client never sent CONNECTION_OPEN then self->address was // never set, so avoid trying to dereference it. Nothing needs to // be cleaned up. if (self->address) { if (*self->address) zsys_info ("client %u address='%s' - de-registering", self->unique_id, self->address); // Cancel all stream subscriptions stream_t *stream = (stream_t *) zlistx_detach (self->readers, NULL); while (stream) { zsock_send (stream->actor, "sp", "CANCEL", self); stream = (stream_t *) zlistx_detach (self->readers, NULL); } // Cancel all service offerings service_t *service = (service_t *) zhashx_first (self->server->services); while (service) { offer_t *offer = (offer_t *) zlistx_first (service->offers); while (offer) { if (offer->client == self) zlistx_delete (service->offers, zlistx_cursor (service->offers)); offer = (offer_t *) zlistx_next (service->offers); } service = (service_t *) zhashx_next (self->server->services); } if (*self->address) zhashx_delete (self->server->clients, self->address); } mlm_proto_set_status_code (self->message, MLM_PROTO_SUCCESS); }
void zloop_ticket_delete (zloop_t *self, void *handle) { s_ticket_t *ticket = (s_ticket_t *) handle; assert (ticket->tag == TICKET_TAG); zlistx_delete (self->tickets, ticket->list_handle); }
void zlistx_purge (zlistx_t *self) { assert (self); while (zlistx_size (self) > 0) zlistx_delete (self, NULL); }
void zloop_poller_end (zloop_t *self, zmq_pollitem_t *item) { assert (self); s_poller_t *poller = (s_poller_t *) zlistx_first (self->pollers); while (poller) { bool match = false; if (item->socket) { if (item->socket == poller->item.socket) match = true; } else { if (item->fd == poller->item.fd) match = true; } if (match) { zlistx_delete (self->pollers, poller->list_handle); // Force rebuild to avoid reading from freed poller self->need_rebuild = true; } poller = (s_poller_t *) zlistx_next (self->pollers); } if (self->verbose) zsys_debug ("zloop: cancel %s poller (%p, %d)", item->socket ? zsys_sockname (zsock_type (item->socket)) : "FD", item->socket, item->fd); }
int ztrie_remove_route (ztrie_t *self, const char *path) { assert (self); ztrie_node_t *match = s_ztrie_parse_path (self, path, MODE_LOOKUP); // The path did match a node which is endpoint to a route if (match && match->endpoint) { // This node is part of other routes, thus it cannot destroy it if (zlistx_size (match->children) > 0) { match->endpoint = false; if (match->data && match->destroy_data_fn) (match->destroy_data_fn) (&match->data); } // If this node is not part of other routes, destroy it else { // Delete match from parent's children before destroying void *handle = zlistx_find (match->parent->children, match); assert (handle); zlistx_delete (match->parent->children, handle); s_ztrie_node_destroy (&match); } return 0; } // If there is no match or the match is not endpoint to a route, fail else return -1; }
static void s_stream_engine_cancel (stream_engine_t *self, void *client) { selector_t *selector = (selector_t *) zlistx_first (self->selectors); while (selector) { void *handle = zlistx_find (selector->clients, client); if (handle) zlistx_delete (selector->clients, handle); selector = (selector_t *) zlistx_next (self->selectors); } }
static void s_timer_remove (zloop_t *self, int timer_id) { s_timer_t *timer = (s_timer_t *) zlistx_first (self->timers); while (timer) { if (timer->timer_id == timer_id) { zlistx_delete (self->timers, timer->list_handle); break; } timer = (s_timer_t *) zlistx_next (self->timers); } }
void zloop_reader_end (zloop_t *self, zsock_t *sock) { assert (self); assert (sock); s_reader_t *reader = (s_reader_t *) zlistx_first (self->readers); while (reader) { if (reader->sock == sock) { zlistx_delete (self->readers, reader->list_handle); self->need_rebuild = true; } reader = (s_reader_t *) zlistx_next (self->readers); } if (self->verbose) zsys_debug ("zloop: cancel %s reader", zsock_type_str (sock)); }
void mlm_server_test (bool verbose) { printf (" * mlm_server: "); if (verbose) printf ("\n"); // @selftest zactor_t *server = zactor_new (mlm_server, "mlm_server_test"); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", "tcp://127.0.0.1:*", NULL); zstr_sendx (server, "PORT", NULL); char *command, *port; int rc = zstr_recvx (server, &command, &port, NULL); assert (rc == 2); assert (streq (command, "PORT")); assert (strlen (port) > 0 && strlen (port) < 6); assert (!streq (port, "-1")); zsock_t *reader = zsock_new (ZMQ_DEALER); assert (reader); zsock_connect (reader, "tcp://127.0.0.1:%s", port); zsock_set_rcvtimeo (reader, 500); mlm_proto_t *proto = mlm_proto_new (); // Server insists that connection starts properly mlm_proto_set_id (proto, MLM_PROTO_STREAM_WRITE); mlm_proto_send (proto, reader); zclock_sleep (500); // to calm things down && make memcheck pass. Thanks @malanka mlm_proto_recv (proto, reader); zclock_sleep (500); // detto as above assert (mlm_proto_id (proto) == MLM_PROTO_ERROR); assert (mlm_proto_status_code (proto) == MLM_PROTO_COMMAND_INVALID); // Now do a stream publish-subscribe test zsock_t *writer = zsock_new (ZMQ_DEALER); assert (writer); zsock_connect (writer, "tcp://127.0.0.1:%s", port); zsock_set_rcvtimeo (reader, 500); // Open connections from both reader and writer mlm_proto_set_id (proto, MLM_PROTO_CONNECTION_OPEN); mlm_proto_send (proto, reader); mlm_proto_recv (proto, reader); assert (mlm_proto_id (proto) == MLM_PROTO_OK); mlm_proto_set_id (proto, MLM_PROTO_CONNECTION_OPEN); mlm_proto_send (proto, writer); mlm_proto_recv (proto, writer); assert (mlm_proto_id (proto) == MLM_PROTO_OK); // Prepare to write and read a "weather" stream mlm_proto_set_id (proto, MLM_PROTO_STREAM_WRITE); mlm_proto_set_stream (proto, "weather"); mlm_proto_send (proto, writer); mlm_proto_recv (proto, writer); assert (mlm_proto_id (proto) == MLM_PROTO_OK); mlm_proto_set_id (proto, MLM_PROTO_STREAM_READ); mlm_proto_set_pattern (proto, "temp.*"); mlm_proto_send (proto, reader); mlm_proto_recv (proto, reader); assert (mlm_proto_id (proto) == MLM_PROTO_OK); // Now send some weather data, with null contents mlm_proto_set_id (proto, MLM_PROTO_STREAM_SEND); mlm_proto_set_subject (proto, "temp.moscow"); mlm_proto_send (proto, writer); mlm_proto_set_subject (proto, "rain.moscow"); mlm_proto_send (proto, writer); mlm_proto_set_subject (proto, "temp.chicago"); mlm_proto_send (proto, writer); mlm_proto_set_subject (proto, "rain.chicago"); mlm_proto_send (proto, writer); mlm_proto_set_subject (proto, "temp.london"); mlm_proto_send (proto, writer); mlm_proto_set_subject (proto, "rain.london"); mlm_proto_send (proto, writer); // We should receive exactly three deliveries, in order mlm_proto_recv (proto, reader); assert (mlm_proto_id (proto) == MLM_PROTO_STREAM_DELIVER); assert (streq (mlm_proto_subject (proto), "temp.moscow")); mlm_proto_recv (proto, reader); assert (mlm_proto_id (proto) == MLM_PROTO_STREAM_DELIVER); assert (streq (mlm_proto_subject (proto), "temp.chicago")); mlm_proto_recv (proto, reader); assert (mlm_proto_id (proto) == MLM_PROTO_STREAM_DELIVER); assert (streq (mlm_proto_subject (proto), "temp.london")); mlm_proto_destroy (&proto); // Finished, we can clean up zsock_destroy (&writer); zsock_destroy (&reader); zactor_destroy (&server); zstr_free (&port); zstr_free (&command); // Test Case: // CLIENTLIST command { const char *endpoint = "inproc://mlm_server_clientlist_test"; zactor_t *server = zactor_new (mlm_server, "mlm_server_clientlist_test"); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", endpoint, NULL); mlm_client_t *client_1 = mlm_client_new (); int rv = mlm_client_connect (client_1, endpoint, 1000, "Karol"); assert (rv >= 0); mlm_client_t *client_2 = mlm_client_new (); rv = mlm_client_connect (client_2, endpoint, 1000, "Tomas"); assert (rv >= 0); mlm_client_t *client_3 = mlm_client_new (); rv = mlm_client_connect (client_3, endpoint, 1000, "Alenka"); assert (rv >= 0); zclock_sleep (500); zstr_sendx (server, "CLIENTLIST", NULL); zmsg_t *message = zmsg_recv (server); assert (message); assert (zmsg_size (message) == 4); char *pop = zmsg_popstr (message); assert (streq (pop, "CLIENTLIST")); zstr_free (&pop); zlistx_t *expected_names = zlistx_new (); assert (expected_names); zlistx_set_destructor (expected_names, (czmq_destructor *) zstr_free); zlistx_set_duplicator (expected_names, (czmq_duplicator *) strdup); zlistx_set_comparator (expected_names, (czmq_comparator *) strcmp); zlistx_add_end (expected_names, (void *) "Karol"); zlistx_add_end (expected_names, (void *) "Tomas"); zlistx_add_end (expected_names, (void *) "Alenka"); pop = zmsg_popstr (message); assert (pop); void *handle = zlistx_find (expected_names, pop); assert (handle); rv = zlistx_delete (expected_names, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_names, pop); assert (handle); rv = zlistx_delete (expected_names, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_names, pop); assert (handle); rv = zlistx_delete (expected_names, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop == NULL); assert (zlistx_size (expected_names) == 0); zmsg_destroy (&message); // remove a client Karol mlm_client_destroy (&client_1); zlistx_add_end (expected_names, (void *) "Tomas"); zlistx_add_end (expected_names, (void *) "Alenka"); zstr_sendx (server, "CLIENTLIST", NULL); zclock_sleep (100); message = zmsg_recv (server); assert (message); assert (zmsg_size (message) == 3); pop = zmsg_popstr (message); assert (streq (pop, "CLIENTLIST")); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_names, pop); assert (handle); rv = zlistx_delete (expected_names, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_names, pop); assert (handle); rv = zlistx_delete (expected_names, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop == NULL); assert (zlistx_size (expected_names) == 0); zlistx_destroy (&expected_names); zmsg_destroy (&message); mlm_client_destroy (&client_2); mlm_client_destroy (&client_3); zactor_destroy (&server); } // Test Case: // STREAMLIST command { const char *endpoint = "inproc://mlm_server_streamlist_test"; zactor_t *server = zactor_new (mlm_server, "mlm_server_streamlist_test"); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", endpoint, NULL); mlm_client_t *client_1 = mlm_client_new (); int rv = mlm_client_connect (client_1, endpoint, 1000, "Karol"); assert (rv != -1); rv = mlm_client_set_producer (client_1, "STREAM_1"); assert (rv != -1); mlm_client_t *client_2 = mlm_client_new (); rv = mlm_client_connect (client_2, endpoint, 1000, "Tomas"); assert (rv != -1); rv = mlm_client_set_producer (client_2, "STREAM_2"); assert (rv != -1); mlm_client_t *client_3 = mlm_client_new (); rv = mlm_client_connect (client_3, endpoint, 1000, "Alenka"); assert (rv != -1); rv = mlm_client_set_consumer (client_3, "STREAM_2", ".*"); assert (rv != -1); zclock_sleep (100); zlistx_t *expected_streams = zlistx_new (); assert (expected_streams); zlistx_set_destructor (expected_streams, (czmq_destructor *) zstr_free); zlistx_set_duplicator (expected_streams, (czmq_duplicator *) strdup); zlistx_set_comparator (expected_streams, (czmq_comparator *) strcmp); zlistx_add_end (expected_streams, (void *) "STREAM_1"); zlistx_add_end (expected_streams, (void *) "STREAM_2"); zstr_sendx (server, "STREAMLIST", NULL); zmsg_t *message = zmsg_recv (server); assert (message); assert (zmsg_size (message) == 3); char *pop = zmsg_popstr (message); assert (streq (pop, "STREAMLIST")); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); void *handle = zlistx_find (expected_streams, pop); assert (handle); rv = zlistx_delete (expected_streams, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_streams, pop); assert (handle); rv = zlistx_delete (expected_streams, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop == NULL); assert (zlistx_size (expected_streams) == 0); zmsg_destroy (&message); // NOTE: Currently when producer disconnects, malamute does not destroy the stream // Therefore it doesn't make sense to test removal of streams, but addition mlm_client_t *client_4 = mlm_client_new (); rv = mlm_client_connect (client_4, endpoint, 1000, "Michal"); assert (rv >= 0); rv = mlm_client_set_producer (client_4, "New stream"); assert (rv >= 0); zlistx_add_end (expected_streams, (void *) "STREAM_1"); zlistx_add_end (expected_streams, (void *) "STREAM_2"); zlistx_add_end (expected_streams, (void *) "New stream"); zclock_sleep (100); zstr_sendx (server, "STREAMLIST", NULL); zclock_sleep (100); message = zmsg_recv (server); assert (message); assert (zmsg_size (message) == 4); pop = zmsg_popstr (message); assert (streq (pop, "STREAMLIST")); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_streams, pop); assert (handle); rv = zlistx_delete (expected_streams, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_streams, pop); assert (handle); rv = zlistx_delete (expected_streams, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_streams, pop); assert (handle); rv = zlistx_delete (expected_streams, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop == NULL); assert (zlistx_size (expected_streams) == 0); zlistx_destroy (&expected_streams); zmsg_destroy (&message); mlm_client_destroy (&client_1); mlm_client_destroy (&client_2); mlm_client_destroy (&client_3); mlm_client_destroy (&client_4); zactor_destroy (&server); } // Regression Test Case: // Segfault from deregistering zombie connection { const char *endpoint = "inproc://mlm_server_deregister_zombie_connection_test"; zactor_t *server = zactor_new (mlm_server, "mlm_server_deregister_zombie_connection_test"); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", endpoint, NULL); zstr_sendx (server, "SET", "server/timeout", "3000", NULL); // 3 second client timeout zsock_t *reader = zsock_new (ZMQ_DEALER); assert (reader); zsock_connect (reader, "inproc://mlm_server_deregister_zombie_connection_test"); zsock_set_rcvtimeo (reader, 500); mlm_proto_t *proto = mlm_proto_new (); // If the malamute server is restarted and clients have queued // up ping messages, the'll be sent before any // CONNECTION_OPEN. The server eventually tries to deregister // this and (previously) would derefence a null pointer for // the client address. mlm_proto_set_id (proto, MLM_PROTO_CONNECTION_PING); mlm_proto_send (proto, reader); printf("Regression test for segfault due to leftover client messages after restart...\n"); // Give the server more than 3 seconds to time out the client... zclock_sleep (3100); printf("passed\n"); mlm_proto_destroy (&proto); zsock_destroy (&reader); zactor_destroy (&server); } { const char *endpoint = "inproc://mlm_server_disconnect_pending_stream_traffic"; zactor_t *server = zactor_new (mlm_server, "mlm_server_disconnect_pending_stream_traffic"); if (verbose) { zstr_send (server, "VERBOSE"); printf("Regression test for use-after-free with pending stream traffic after disconnect\n"); } zstr_sendx (server, "BIND", endpoint, NULL); mlm_client_t *producer = mlm_client_new (); assert (mlm_client_connect (producer, endpoint, 1000, "producer") >= 0); assert (mlm_client_set_producer (producer, "STREAM_TEST") >= 0); zstr_sendx (server, "SLOW_TEST_MODE", NULL); mlm_client_t *consumer = mlm_client_new (); assert (mlm_client_connect (consumer, endpoint, 1000, "consumer") >= 0); assert (mlm_client_set_consumer (consumer, "STREAM_TEST", ".*") >= 0); zmsg_t *msg = zmsg_new (); zmsg_addstr (msg, "world"); assert (mlm_client_send (producer, "hello", &msg) >= 0); mlm_client_destroy (&consumer); zclock_sleep (2000); mlm_client_destroy (&producer); zactor_destroy (&server); } // @end printf ("OK\n"); }
static void handler (zsock_t *pipe, void *args) { curl_global_init(CURL_GLOBAL_ALL); CURLM *multi = curl_multi_init (); CURLSH *share = curl_share_init (); curl_share_setopt (share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); curl_share_setopt (share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); curl_share_setopt (share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); long verbose = (*(bool *) args) ? 1L : 0L; long timeout = 30; CURLMcode code; SOCKET pipefd = zsock_fd (pipe); struct curl_waitfd waitfd = {pipefd, CURL_WAIT_POLLIN}; // List to hold pending curl handles, in case we are destroy the client // while request are inprogress zlistx_t *pending_handles = zlistx_new (); zlistx_set_destructor (pending_handles, (zlistx_destructor_fn *) curl_destructor); zsock_signal (pipe, 0); bool terminated = false; while (!terminated) { int events = zsock_events (pipe); if ((events & ZMQ_POLLIN) == 0) { code = curl_multi_wait (multi, &waitfd, 1, 1000, NULL); assert (code == CURLM_OK); } events = zsock_events (pipe); if (events & ZMQ_POLLIN) { char* command = zstr_recv (pipe); if (!command) break; // Interrupted // All actors must handle $TERM in this way if (streq (command, "$TERM")) terminated = true; else if (streq (command, "GET")) { char *url; zlistx_t *headers; void *userp; int rc = zsock_recv (pipe, "slp", &url, &headers, &userp); assert (rc == 0); zchunk_t *data = zchunk_new (NULL, 100); assert (data); struct curl_slist *curl_headers = zlistx_to_slist (headers); CURL *curl = curl_easy_init (); zlistx_add_end (pending_handles, curl); http_request *request = (http_request *) zmalloc (sizeof (http_request)); assert (request); request->userp = userp; request->curl = curl; request->data = data; request->headers = curl_headers; curl_easy_setopt (curl, CURLOPT_SHARE, share); curl_easy_setopt (curl, CURLOPT_TIMEOUT, timeout); curl_easy_setopt (curl, CURLOPT_VERBOSE, verbose); curl_easy_setopt (curl, CURLOPT_HTTPHEADER, curl_headers); curl_easy_setopt (curl, CURLOPT_URL, url); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt (curl, CURLOPT_WRITEDATA, data); curl_easy_setopt (curl, CURLOPT_PRIVATE, request); code = curl_multi_add_handle (multi, curl); assert (code == CURLM_OK); zlistx_destroy (&headers); zstr_free (&url); } else { puts ("E: invalid message to actor"); assert (false); } zstr_free (&command); } int still_running; code = curl_multi_perform (multi, &still_running); assert (code == CURLM_OK); int msgq = 0; struct CURLMsg *msg = curl_multi_info_read(multi, &msgq); while (msg) { if(msg->msg == CURLMSG_DONE) { CURL *curl = msg->easy_handle; http_request *request; curl_easy_getinfo(curl, CURLINFO_PRIVATE, &request); long response_code_long; curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &response_code_long); int response_code = (int)response_code_long; int rc = zsock_send (pipe, "icp", response_code, request->data, request->userp); assert (rc == 0); curl_multi_remove_handle (multi, curl); // Remove curl from the pending handles and delete it void *handle = zlistx_find (pending_handles, curl); assert (handle); rc = zlistx_delete (pending_handles, handle); assert (rc == 0); } msg = curl_multi_info_read(multi, &msgq); } } zlistx_destroy (&pending_handles); curl_share_cleanup (share); curl_multi_cleanup (multi); curl_global_cleanup (); }
int zloop_start (zloop_t *self) { assert (self); int rc = 0; // Main reactor loop while (!zsys_interrupted) { if (self->need_rebuild) { // If s_rebuild_pollset() fails, break out of the loop and // return its error rc = s_rebuild_pollset (self); if (rc) break; } rc = zmq_poll (self->pollset, (int) self->poll_size, s_tickless (self)); if (rc == -1 || zsys_interrupted) { if (self->verbose) zsys_debug ("zloop: interrupted"); rc = 0; break; // Context has been shut down } // Handle any timers that have now expired int64_t time_now = zclock_mono (); s_timer_t *timer = (s_timer_t *) zlistx_first (self->timers); while (timer) { if (time_now >= timer->when) { if (self->verbose) zsys_debug ("zloop: call timer handler id=%d", timer->timer_id); rc = timer->handler (self, timer->timer_id, timer->arg); if (rc == -1) break; // Timer handler signaled break if (timer->times && --timer->times == 0) zlistx_delete (self->timers, timer->list_handle); else timer->when += timer->delay; } timer = (s_timer_t *) zlistx_next (self->timers); } // Handle any tickets that have now expired s_ticket_t *ticket = (s_ticket_t *) zlistx_first (self->tickets); while (ticket && time_now >= ticket->when) { if (self->verbose) zsys_debug ("zloop: call ticket handler"); rc = ticket->handler (self, 0, ticket->arg); if (rc == -1) break; // Timer handler signaled break zlistx_delete (self->tickets, ticket->list_handle); ticket = (s_ticket_t *) zlistx_next (self->tickets); } // Handle any readers and pollers that are ready size_t item_nbr; for (item_nbr = 0; item_nbr < self->poll_size && rc >= 0; item_nbr++) { s_reader_t *reader = &self->readact [item_nbr]; if (reader->handler) { if ((self->pollset [item_nbr].revents & ZMQ_POLLERR) && !reader->tolerant) { if (self->verbose) zsys_warning ("zloop: can't read %s socket: %s", zsock_type_str (reader->sock), zmq_strerror (zmq_errno ())); // Give handler one chance to handle error, then kill // reader because it'll disrupt the reactor otherwise. if (reader->errors++) { zloop_reader_end (self, reader->sock); self->pollset [item_nbr].revents = 0; } } else reader->errors = 0; // A non-error happened if (self->pollset [item_nbr].revents) { if (self->verbose) zsys_debug ("zloop: call %s socket handler", zsock_type_str (reader->sock)); rc = reader->handler (self, reader->sock, reader->arg); if (rc == -1 || self->need_rebuild) break; } } else { s_poller_t *poller = &self->pollact [item_nbr]; assert (self->pollset [item_nbr].socket == poller->item.socket); if ((self->pollset [item_nbr].revents & ZMQ_POLLERR) && !poller->tolerant) { if (self->verbose) zsys_warning ("zloop: can't poll %s socket (%p, %d): %s", poller->item.socket ? zsys_sockname (zsock_type (poller->item.socket)) : "FD", poller->item.socket, poller->item.fd, zmq_strerror (zmq_errno ())); // Give handler one chance to handle error, then kill // poller because it'll disrupt the reactor otherwise. if (poller->errors++) { zloop_poller_end (self, &poller->item); self->pollset [item_nbr].revents = 0; } } else poller->errors = 0; // A non-error happened if (self->pollset [item_nbr].revents) { if (self->verbose) zsys_debug ("zloop: call %s socket handler (%p, %d)", poller->item.socket ? zsys_sockname (zsock_type (poller->item.socket)) : "FD", poller->item.socket, poller->item.fd); rc = poller->handler (self, &self->pollset [item_nbr], poller->arg); if (rc == -1 || self->need_rebuild) break; } } } // Now handle any timer zombies // This is going to be slow if we have many timers; we might use // a faster lookup on the timer list. while (zlistx_first (self->zombies)) { // Get timer_id back from pointer int timer_id = (byte *) zlistx_detach (self->zombies, NULL) - (byte *) NULL; s_timer_remove (self, timer_id); } if (rc == -1) break; } self->terminated = true; return rc; }
void zlistx_test (bool verbose) { printf (" * zlistx: "); // @selftest zlistx_t *list = zlistx_new (); assert (list); assert (zlistx_size (list) == 0); // Test operations on an empty list assert (zlistx_first (list) == NULL); assert (zlistx_last (list) == NULL); assert (zlistx_next (list) == NULL); assert (zlistx_prev (list) == NULL); assert (zlistx_find (list, "hello") == NULL); assert (zlistx_delete (list, NULL) == -1); assert (zlistx_detach (list, NULL) == NULL); assert (zlistx_delete (list, NULL) == -1); assert (zlistx_detach (list, NULL) == NULL); zlistx_purge (list); zlistx_sort (list); // Use item handlers zlistx_set_destructor (list, (zlistx_destructor_fn *) zstr_free); zlistx_set_duplicator (list, (zlistx_duplicator_fn *) strdup); zlistx_set_comparator (list, (zlistx_comparator_fn *) strcmp); // Try simple insert/sort/delete/next assert (zlistx_next (list) == NULL); zlistx_add_end (list, "world"); assert (streq ((char *) zlistx_next (list), "world")); zlistx_add_end (list, "hello"); assert (streq ((char *) zlistx_prev (list), "hello")); zlistx_sort (list); assert (zlistx_size (list) == 2); void *handle = zlistx_find (list, "hello"); char *item1 = (char *) zlistx_item (list); char *item2 = (char *) zlistx_handle_item (handle); assert (item1 == item2); assert (streq (item1, "hello")); zlistx_delete (list, handle); assert (zlistx_size (list) == 1); char *string = (char *) zlistx_detach (list, NULL); assert (streq (string, "world")); free (string); assert (zlistx_size (list) == 0); // Check next/back work // Now populate the list with items zlistx_add_start (list, "five"); zlistx_add_end (list, "six"); zlistx_add_start (list, "four"); zlistx_add_end (list, "seven"); zlistx_add_start (list, "three"); zlistx_add_end (list, "eight"); zlistx_add_start (list, "two"); zlistx_add_end (list, "nine"); zlistx_add_start (list, "one"); zlistx_add_end (list, "ten"); // Test our navigation skills assert (zlistx_size (list) == 10); assert (streq ((char *) zlistx_last (list), "ten")); assert (streq ((char *) zlistx_prev (list), "nine")); assert (streq ((char *) zlistx_prev (list), "eight")); assert (streq ((char *) zlistx_prev (list), "seven")); assert (streq ((char *) zlistx_prev (list), "six")); assert (streq ((char *) zlistx_prev (list), "five")); assert (streq ((char *) zlistx_first (list), "one")); assert (streq ((char *) zlistx_next (list), "two")); assert (streq ((char *) zlistx_next (list), "three")); assert (streq ((char *) zlistx_next (list), "four")); // Sort by alphabetical order zlistx_sort (list); assert (streq ((char *) zlistx_first (list), "eight")); assert (streq ((char *) zlistx_last (list), "two")); // Moving items around handle = zlistx_find (list, "six"); zlistx_move_start (list, handle); assert (streq ((char *) zlistx_first (list), "six")); zlistx_move_end (list, handle); assert (streq ((char *) zlistx_last (list), "six")); zlistx_sort (list); assert (streq ((char *) zlistx_last (list), "two")); // Copying a list zlistx_t *copy = zlistx_dup (list); assert (copy); assert (zlistx_size (copy) == 10); assert (streq ((char *) zlistx_first (copy), "eight")); assert (streq ((char *) zlistx_last (copy), "two")); zlistx_destroy (©); // Delete items while iterating string = (char *) zlistx_first (list); assert (streq (string, "eight")); string = (char *) zlistx_next (list); assert (streq (string, "five")); zlistx_delete (list, zlistx_cursor (list)); string = (char *) zlistx_next (list); assert (streq (string, "four")); zlistx_purge (list); zlistx_destroy (&list); // @end printf ("OK\n"); }
/// // Delete an item, using its handle. Calls the item destructor is any is // set. If handle is null, deletes the first item on the list. Returns 0 // if an item was deleted, -1 if not. If cursor was at item, moves cursor // to previous item, so you can delete items while iterating forwards // through a list. int QZlistx::deleteNoConflict (void *handle) { int rv = zlistx_delete (self, handle); return rv; }