changeset 1897:1e6ed8045f2b HEAD

Changed hash_foreach() to iterator.
author Timo Sirainen <tss@iki.fi>
date Wed, 03 Dec 2003 02:40:21 +0200
parents 0613ad9d5034
children 54e60d63a942
files src/auth/auth-client-connection.c src/auth/db-ldap.c src/imap-login/client.c src/imap/imap-thread.c src/lib-index/maildir/maildir-sync.c src/lib/hash.c src/lib/hash.h src/login-common/ssl-proxy-gnutls.c src/login-common/ssl-proxy-openssl.c src/master/auth-process.c src/master/login-process.c src/pop3-login/client.c
diffstat 12 files changed, 292 insertions(+), 246 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/auth-client-connection.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/auth/auth-client-connection.c	Wed Dec 03 02:40:21 2003 +0200
@@ -207,14 +207,6 @@
 	return conn;
 }
 
-static void auth_request_hash_destroy(void *key __attr_unused__, void *value,
-				      void *context __attr_unused__)
-{
-	struct auth_request *auth_request = value;
-
-	auth_request->conn = NULL;
-}
-
 void auth_client_connection_destroy(struct auth_client_connection *conn)
 {
 	struct auth_client_connection **pos;
@@ -244,10 +236,19 @@
 
 static void auth_client_connection_unref(struct auth_client_connection *conn)
 {
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
 	if (--conn->refcount > 0)
 		return;
 
-	hash_foreach(conn->auth_requests, auth_request_hash_destroy, NULL);
+	iter = hash_iterate_init(conn->auth_requests);
+	while (hash_iterate(iter, &key, &value)) {
+		struct auth_request *auth_request = value;
+
+		auth_request->conn = NULL;
+	}
+	hash_iterate_deinit(iter);
 	hash_destroy(conn->auth_requests);
 
 	i_stream_unref(conn->input);
@@ -256,20 +257,32 @@
 	pool_unref(conn->pool);
 }
 
-static void auth_request_hash_timeout_check(void *key __attr_unused__,
-					    void *value, void *context)
+static void
+auth_client_connection_check_timeouts(struct auth_client_connection *conn)
 {
-	struct auth_client_connection *conn = context;
-	struct auth_request *auth_request = value;
+	struct hash_iterate_context *iter;
+	void *key, *value;
+	unsigned int secs;
+	int destroy = FALSE;
+
+	iter = hash_iterate_init(conn->auth_requests);
+	while (hash_iterate(iter, &key, &value)) {
+		struct auth_request *auth_request = value;
 
-	if (auth_request->created + AUTH_REQUEST_TIMEOUT < ioloop_time) {
-		i_warning("Login process has too old (%us) requests, "
-			  "killing it.",
-			  (unsigned int)(ioloop_time - auth_request->created));
+		if (auth_request->created + AUTH_REQUEST_TIMEOUT < ioloop_time) {
+			secs = (unsigned int) (ioloop_time -
+					       auth_request->created);
+			i_warning("Login process has too old (%us) requests, "
+				  "killing it.", secs);
 
+			destroy = TRUE;
+			break;
+		}
+	}
+	hash_iterate_deinit(iter);
+
+	if (destroy)
 		auth_client_connection_destroy(conn);
-		hash_foreach_stop();
-	}
 }
 
 static void request_timeout(void *context __attr_unused__)
@@ -279,11 +292,7 @@
 
 	for (conn = master->clients; conn != NULL; conn = next) {
 		next = conn->next;
-
-		conn->refcount++;
-		hash_foreach(conn->auth_requests,
-			     auth_request_hash_timeout_check, conn);
-		auth_client_connection_unref(conn);
+		auth_client_connection_check_timeouts(conn);
 	}
 }
 
--- a/src/auth/db-ldap.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/auth/db-ldap.c	Wed Dec 03 02:40:21 2003 +0200
@@ -226,19 +226,19 @@
 	return TRUE;
 }
 
-static void hash_ldap_request_destroy(void *key __attr_unused__,
-				      void *value, void *context)
-{
-	struct ldap_request *request = value;
-	struct ldap_connection *conn = context;
-
-	request->callback(conn, request, NULL);
-	i_free(request);
-}
-
 static void ldap_conn_close(struct ldap_connection *conn)
 {
-	hash_foreach(conn->requests, hash_ldap_request_destroy, conn);
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(conn->requests);
+	while (hash_iterate(iter, &key, &value)) {
+		struct ldap_request *request = value;
+
+		request->callback(conn, request, NULL);
+		i_free(request);
+	}
+	hash_iterate_deinit(iter);
 	hash_clear(conn->requests, FALSE);
 
 	conn->connected = FALSE;
--- a/src/imap-login/client.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/imap-login/client.c	Wed Dec 03 02:40:21 2003 +0200
@@ -309,45 +309,40 @@
 		o_stream_flush(client->output);
 }
 
-static void client_hash_destroy_oldest(void *key, void *value __attr_unused__,
-				       void *context)
+static void client_destroy_oldest(void)
 {
-	struct imap_client *client = key;
-	struct imap_client *const *destroy_clients;
-	buffer_t *destroy_buf = context;
-	size_t i, count;
+	struct hash_iterate_context *iter;
+	void *key, *value;
+	struct imap_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
+	int i;
+
+	/* find the oldest clients and put them to destroy-buffer */
+	memset(destroy_buf, 0, sizeof(destroy_buf));
 
-	destroy_clients = buffer_get_data(destroy_buf, &count);
-	count /= sizeof(struct imap_client *);
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct imap_client *client = key;
 
-	for (i = 0; i < count; i++) {
-		if (destroy_clients[i]->created > client->created) {
-			buffer_insert(destroy_buf,
-				      i * sizeof(struct imap_client *),
-				      &client, sizeof(struct imap_client *));
-			break;
+		for (i = 0; i < CLIENT_DESTROY_OLDEST_COUNT; i++) {
+			if (destroy_buf[i] == NULL ||
+			    destroy_buf[i]->created > client->created) {
+				/* @UNSAFE */
+				memmove(destroy_buf+i+1, destroy_buf+i,
+					sizeof(destroy_buf) -
+					(i+1) * sizeof(struct imap_client *));
+				destroy_buf[i] = client;
+				break;
+			}
 		}
 	}
-}
-
-static void client_destroy_oldest(void)
-{
-	struct imap_client *const *destroy_clients;
-	buffer_t *destroy_buf;
-	size_t i, count;
-
-	/* find the oldest clients and put them to destroy-buffer */
-	destroy_buf = buffer_create_static_hard(pool_datastack_create(),
-						sizeof(struct imap_client *) *
-						CLIENT_DESTROY_OLDEST_COUNT);
-	hash_foreach(clients, client_hash_destroy_oldest, destroy_buf);
+	hash_iterate_deinit(iter);
 
 	/* then kill them */
-	destroy_clients = buffer_get_data(destroy_buf, &count);
-	count /= sizeof(struct imap_client *);
+	for (i = 0; i < CLIENT_DESTROY_OLDEST_COUNT; i++) {
+		if (destroy_buf[i] == NULL)
+			break;
 
-	for (i = 0; i < count; i++) {
-		client_destroy(destroy_clients[i],
+		client_destroy(destroy_buf[i],
 			       "Disconnected: Connection queue full");
 	}
 }
@@ -474,11 +469,8 @@
 	i_info("%s [%s]", text, addr);
 }
 
-static void client_hash_check_idle(void *key, void *value __attr_unused__,
-				   void *context __attr_unused__)
+static void client_check_idle(struct imap_client *client)
 {
-	struct imap_client *client = key;
-
 	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
 		client_send_line(client, "* BYE Disconnected for inactivity.");
 		client_destroy(client, "Disconnected: Inactivity");
@@ -487,7 +479,16 @@
 
 static void idle_timeout(void *context __attr_unused__)
 {
-	hash_foreach(clients, client_hash_check_idle, NULL);
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct imap_client *client = key;
+
+		client_check_idle(client);
+	}
+	hash_iterate_deinit(iter);
 }
 
 unsigned int clients_get_count(void)
@@ -495,31 +496,35 @@
 	return hash_size(clients);
 }
 
-static void client_hash_check_io(void *key, void *value __attr_unused__,
-				 void *context __attr_unused__)
-{
-	struct imap_client *client = key;
-
-	if (client->input_blocked) {
-		client->input_blocked = FALSE;
-		client_input(client);
-	}
-}
-
 void clients_notify_auth_connected(void)
 {
-	hash_foreach(clients, client_hash_check_io, NULL);
-}
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct imap_client *client = key;
 
-static void client_hash_destroy(void *key, void *value __attr_unused__,
-				void *context __attr_unused__)
-{
-	client_destroy(key, NULL);
+		if (client->input_blocked) {
+			client->input_blocked = FALSE;
+			client_input(client);
+		}
+	}
+	hash_iterate_deinit(iter);
 }
 
 void clients_destroy_all(void)
 {
-	hash_foreach(clients, client_hash_destroy, NULL);
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct imap_client *client = key;
+
+		client_destroy(client, NULL);
+	}
+	hash_iterate_deinit(iter);
 }
 
 void clients_init(void)
--- a/src/imap/imap-thread.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/imap/imap-thread.c	Wed Dec 03 02:40:21 2003 +0200
@@ -898,21 +898,21 @@
 	(void)o_stream_send(ctx->output, str_data(str), str_len(str));
 }
 
-static void save_root_cb(void *key __attr_unused__, void *value, void *context)
-{
-	struct thread_context *ctx = context;
-	struct node *node = value;
-
-	if (node->parent == NULL)
-		add_root(ctx, node);
-}
-
 static void mail_thread_finish(struct thread_context *ctx)
 {
+	struct hash_iterate_context *iter;
+	void *key, *value;
 	struct node *node;
 
 	/* (2) save root nodes and drop the msgids */
-	hash_foreach(ctx->msgid_hash, save_root_cb, ctx);
+	iter = hash_iterate_init(ctx->msgid_hash);
+	while (hash_iterate(iter, &key, &value)) {
+		struct node *node = value;
+
+		if (node->parent == NULL)
+			add_root(ctx, node);
+	}
+	hash_iterate_deinit(iter);
 
 	/* drop the memory allocated for message-IDs and msgid_hash,
 	   reuse their memory for base subjects */
--- a/src/lib-index/maildir/maildir-sync.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/lib-index/maildir/maildir-sync.c	Wed Dec 03 02:40:21 2003 +0200
@@ -361,15 +361,6 @@
 	return TRUE;
 }
 
-static void uidlist_hash_get_filenames(void *key, void *value, void *context)
-{
-	buffer_t *buf = context;
-	struct maildir_hash_rec *hash_rec = value;
-
-	if (ACTION(hash_rec) == MAILDIR_FILE_ACTION_NEW)
-		buffer_append(buf, (const void *) &key, sizeof(const char *));
-}
-
 static int maildir_time_cmp(const void *p1, const void *p2)
 {
 	const char *s1 = *((const char **) p1);
@@ -393,6 +384,8 @@
 
 static int maildir_full_sync_finish_new_mails(struct maildir_sync_context *ctx)
 {
+	struct hash_iterate_context *iter;
+	void *key, *value;
 	const char *dir, **new_files;
 	buffer_t *buf;
 	unsigned int i;
@@ -404,7 +397,16 @@
 	   so we should get them to same order as they were created. */
 	buf = buffer_create_static_hard(ctx->pool,
 					ctx->new_count * sizeof(const char *));
-	hash_foreach(ctx->files, uidlist_hash_get_filenames, buf);
+	iter = hash_iterate_init(ctx->files);
+	while (hash_iterate(iter, &key, &value)) {
+		struct maildir_hash_rec *hash_rec = value;
+
+		if (ACTION(hash_rec) == MAILDIR_FILE_ACTION_NEW) {
+			buffer_append(buf, (const void *) &key,
+				      sizeof(const char *));
+		}
+	}
+	hash_iterate_deinit(iter);
 	i_assert(buffer_get_used_size(buf) ==
 		 ctx->new_count * sizeof(const char *));
 
@@ -781,33 +783,11 @@
 	return ret;
 }
 
-static void maildir_sync_hash_fix_allocs(void *key, void *value, void *context)
-{
-        struct maildir_sync_context *ctx = context;
-	struct maildir_hash_rec *hash_rec = value;
-
-	switch (ACTION(hash_rec)) {
-	case MAILDIR_FILE_ACTION_NONE:
-		hash_remove(ctx->files, key);
-		break;
-	case MAILDIR_FILE_ACTION_EXPUNGE:
-		if (hash_rec->action & MAILDIR_FILE_FLAG_ALLOCED) {
-			/* we're getting here because our recently
-			   inserted node is traversed as well */
-			break;
-		}
-
-		hash_rec->action |= MAILDIR_FILE_FLAG_ALLOCED;
-		hash_insert(ctx->files, p_strdup(ctx->pool, key), value);
-		break;
-	default:
-		break;
-	}
-}
-
 static int maildir_full_sync_dir(struct maildir_sync_context *ctx,
 				 int new_dir, DIR *dirp, struct dirent *d)
 {
+	struct hash_iterate_context *iter;
+	void *key, *value;
 	struct maildir_hash_rec *hash_rec;
 	void *orig_key, *orig_value;
 	int newflag;
@@ -864,7 +844,30 @@
 	/* records that are left to hash must not have any (filename) pointers
 	   to cache file. So remove none actions, and p_strdup() expunge
 	   actions. */
-	hash_foreach(ctx->files, maildir_sync_hash_fix_allocs, ctx);
+	iter = hash_iterate_init(ctx->files);
+	while (hash_iterate(iter, &key, &value)) {
+		struct maildir_hash_rec *hash_rec = value;
+
+		switch (ACTION(hash_rec)) {
+		case MAILDIR_FILE_ACTION_NONE:
+			hash_remove(ctx->files, key);
+			break;
+		case MAILDIR_FILE_ACTION_EXPUNGE:
+			if (hash_rec->action & MAILDIR_FILE_FLAG_ALLOCED) {
+				/* we're getting here because our recently
+				   inserted node is traversed as well */
+				break;
+			}
+
+			hash_rec->action |= MAILDIR_FILE_FLAG_ALLOCED;
+			hash_insert(ctx->files,
+				    p_strdup(ctx->pool, key), value);
+			break;
+		default:
+			break;
+		}
+	}
+	hash_iterate_deinit(iter);
 
 	return TRUE;
 }
--- a/src/lib/hash.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/lib/hash.c	Wed Dec 03 02:40:21 2003 +0200
@@ -32,8 +32,6 @@
 
 static int hash_resize(struct hash_table *table, int grow);
 
-static int foreach_stop;
-
 static int direct_cmp(const void *p1, const void *p2)
 {
 	return p1 == p2 ? 0 : 1;
@@ -321,37 +319,65 @@
 	return table->nodes_count;
 }
 
-void hash_foreach(struct hash_table *table, hash_foreach_callback_t *callback,
-		  void *context)
+struct hash_iterate_context {
+	struct hash_table *table;
+	struct hash_node *next;
+	size_t pos;
+};
+
+struct hash_iterate_context *hash_iterate_init(struct hash_table *table)
 {
-	struct hash_node *node;
-	size_t i;
+	struct hash_iterate_context *ctx;
 
 	hash_freeze(table);
 
-	foreach_stop = FALSE;
-
-	for (i = 0; i < table->size; i++) {
-		node = &table->nodes[i];
+	ctx = i_new(struct hash_iterate_context, 1);
+	ctx->table = table;
+	ctx->next = &table->nodes[0];
+	return ctx;
+}
 
-		do {
-			if (node->key != NULL) {
-				callback(node->key, node->value, context);
-				if (foreach_stop) {
-					table->frozen--;
-					return;
-				}
+static struct hash_node *hash_iterate_next(struct hash_iterate_context *ctx,
+					   struct hash_node *node)
+{
+	do {
+		if (node == NULL) {
+			if (++ctx->pos == ctx->table->size) {
+				ctx->pos--;
+				return NULL;
 			}
+			node = &ctx->table->nodes[ctx->pos];
+		} else {
 			node = node->next;
-		} while (node != NULL);
-	}
+		}
+	} while (node->key == NULL);
 
-	hash_thaw(table);
+	return node;
 }
 
-void hash_foreach_stop(void)
+int hash_iterate(struct hash_iterate_context *ctx,
+		 void **key_r, void **value_r)
 {
-        foreach_stop = TRUE;
+	struct hash_node *node;
+
+	node = ctx->next;
+	if (node != NULL && node->key == NULL)
+		node = hash_iterate_next(ctx, node);
+	if (node == NULL) {
+		*key_r = *value_r = NULL;
+		return FALSE;
+	}
+	*key_r = node->key;
+	*value_r = node->value;
+
+	ctx->next = hash_iterate_next(ctx, node);
+	return TRUE;
+}
+
+void hash_iterate_deinit(struct hash_iterate_context *ctx)
+{
+	hash_thaw(ctx->table);
+	i_free(ctx);
 }
 
 void hash_freeze(struct hash_table *table)
--- a/src/lib/hash.h	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/lib/hash.h	Wed Dec 03 02:40:21 2003 +0200
@@ -5,7 +5,6 @@
 typedef unsigned int hash_callback_t(const void *p);
 /* Returns 0 if the pointers are equal. */
 typedef int hash_cmp_callback_t(const void *p1, const void *p2);
-typedef void hash_foreach_callback_t(void *key, void *value, void *context);
 
 /* Create a new hash table. If initial_size is 0, the default value is used.
    If hash_cb or key_compare_cb is NULL, direct hashing/comparing is used.
@@ -35,13 +34,13 @@
 void hash_remove(struct hash_table *table, const void *key);
 size_t hash_size(struct hash_table *table);
 
-/* Calls the given function for each node in hash table. You may safely
-   call hash_*() functions inside your function, but if you add any
-   new nodes, they may or may not be called for in this foreach loop. */
-void hash_foreach(struct hash_table *table,
-		  hash_foreach_callback_t *callback, void *context);
-/* Stop the active hash_foreach() loop */
-void hash_foreach_stop(void);
+/* Iterates through all nodes in hash table. You may safely call hash_*()
+   functions while iterating, but if you add any new nodes, they may or may
+   not be called for in this iteration. */
+struct hash_iterate_context *hash_iterate_init(struct hash_table *table);
+int hash_iterate(struct hash_iterate_context *ctx,
+		 void **key_r, void **value_r);
+void hash_iterate_deinit(struct hash_iterate_context *ctx);
 
 /* Hash table isn't resized, and removed nodes aren't removed from
    the list while hash table is freezed. Supports nesting. */
--- a/src/login-common/ssl-proxy-gnutls.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/login-common/ssl-proxy-gnutls.c	Wed Dec 03 02:40:21 2003 +0200
@@ -527,18 +527,18 @@
 	ssl_initialized = TRUE;
 }
 
-static void ssl_proxy_destroy_hash(void *key __attr_unused__, void *value,
-				   void *context __attr_unused__)
-{
-	ssl_proxy_destroy(value);
-}
-
 void ssl_proxy_deinit(void)
 {
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
 	if (!ssl_initialized)
 		return;
 
-	hash_foreach(ssl_proxies, ssl_proxy_destroy_hash, NULL);
+	iter = hash_iterate_init(ssl_proxies);
+	while (hash_iterate(iter, &key, &value))
+		ssl_proxy_destroy(value);
+	hash_iterate_deinit(iter);
 	hash_destroy(ssl_proxies);
 
 	gnutls_certificate_free_cred(x509_cred);
--- a/src/login-common/ssl-proxy-openssl.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/login-common/ssl-proxy-openssl.c	Wed Dec 03 02:40:21 2003 +0200
@@ -451,18 +451,18 @@
 	ssl_initialized = TRUE;
 }
 
-static void ssl_proxy_destroy_hash(void *key __attr_unused__, void *value,
-				   void *context __attr_unused__)
-{
-	ssl_proxy_unref(value);
-}
-
 void ssl_proxy_deinit(void)
 {
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
 	if (!ssl_initialized)
 		return;
 
-	hash_foreach(ssl_proxies, ssl_proxy_destroy_hash, NULL);
+	iter = hash_iterate_init(ssl_proxies);
+	while (hash_iterate(iter, &key, &value))
+		ssl_proxy_destroy(value);
+	hash_iterate_deinit(iter);
 	hash_destroy(ssl_proxies);
 
 	SSL_CTX_free(ssl_ctx);
--- a/src/master/auth-process.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/master/auth-process.c	Wed Dec 03 02:40:21 2003 +0200
@@ -216,14 +216,10 @@
 	return p;
 }
 
-static void request_hash_destroy(void *key __attr_unused__,
-				 void *value, void *context __attr_unused__)
-{
-	auth_master_callback(NULL, NULL, value);
-}
-
 static void auth_process_destroy(struct auth_process *p)
 {
+	struct hash_iterate_context *iter;
+	void *key, *value;
 	struct auth_process **pos;
 
 	if (!p->initialized && io_loop_is_running(ioloop)) {
@@ -239,7 +235,10 @@
 	}
 	p->group->process_count--;
 
-	hash_foreach(p->requests, request_hash_destroy, NULL);
+	iter = hash_iterate_init(p->requests);
+	while (hash_iterate(iter, &key, &value))
+		auth_master_callback(NULL, NULL, value);
+	hash_iterate_deinit(iter);
 	hash_destroy(p->requests);
 
 	i_stream_unref(p->input);
--- a/src/master/login-process.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/master/login-process.c	Wed Dec 03 02:40:21 2003 +0200
@@ -506,15 +506,15 @@
 		p->group->wanted_processes_count = 0;
 }
 
-static void login_hash_destroy(void *key __attr_unused__, void *value,
-			       void *context __attr_unused__)
-{
-	login_process_destroy(value);
-}
-
 void login_processes_destroy_all(void)
 {
-	hash_foreach(processes, login_hash_destroy, NULL);
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(processes);
+	while (hash_iterate(iter, &key, &value))
+		login_process_destroy(value);
+	hash_iterate_deinit(iter);
 
 	while (login_groups != NULL) {
 		struct login_group *group = login_groups;
--- a/src/pop3-login/client.c	Mon Dec 01 21:07:34 2003 +0200
+++ b/src/pop3-login/client.c	Wed Dec 03 02:40:21 2003 +0200
@@ -189,45 +189,40 @@
 		o_stream_flush(client->output);
 }
 
-static void client_hash_destroy_oldest(void *key, void *value __attr_unused__,
-				       void *context)
+static void client_destroy_oldest(void)
 {
-	struct pop3_client *client = key;
-	struct pop3_client *const *destroy_clients;
-	buffer_t *destroy_buf = context;
-	size_t i, count;
+	struct hash_iterate_context *iter;
+	void *key, *value;
+	struct pop3_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
+	int i;
+
+	/* find the oldest clients and put them to destroy-buffer */
+	memset(destroy_buf, 0, sizeof(destroy_buf));
 
-	destroy_clients = buffer_get_data(destroy_buf, &count);
-	count /= sizeof(struct pop3_client *);
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct pop3_client *client = key;
 
-	for (i = 0; i < count; i++) {
-		if (destroy_clients[i]->created > client->created) {
-			buffer_insert(destroy_buf,
-				      i * sizeof(struct pop3_client *),
-				      &client, sizeof(struct pop3_client *));
-			break;
+		for (i = 0; i < CLIENT_DESTROY_OLDEST_COUNT; i++) {
+			if (destroy_buf[i] == NULL ||
+			    destroy_buf[i]->created > client->created) {
+				/* @UNSAFE */
+				memmove(destroy_buf+i+1, destroy_buf+i,
+					sizeof(destroy_buf) -
+					(i+1) * sizeof(struct pop3_client *));
+				destroy_buf[i] = client;
+				break;
+			}
 		}
 	}
-}
-
-static void client_destroy_oldest(void)
-{
-	struct pop3_client *const *destroy_clients;
-	buffer_t *destroy_buf;
-	size_t i, count;
-
-	/* find the oldest clients and put them to destroy-buffer */
-	destroy_buf = buffer_create_static_hard(pool_datastack_create(),
-						sizeof(struct pop3_client *) *
-						CLIENT_DESTROY_OLDEST_COUNT);
-	hash_foreach(clients, client_hash_destroy_oldest, destroy_buf);
+	hash_iterate_deinit(iter);
 
 	/* then kill them */
-	destroy_clients = buffer_get_data(destroy_buf, &count);
-	count /= sizeof(struct pop3_client *);
+	for (i = 0; i < CLIENT_DESTROY_OLDEST_COUNT; i++) {
+		if (destroy_buf[i] == NULL)
+			break;
 
-	for (i = 0; i < count; i++) {
-		client_destroy(destroy_clients[i],
+		client_destroy(destroy_buf[i],
 			       "Disconnected: Connection queue full");
 	}
 }
@@ -344,18 +339,24 @@
 	i_info("%s [%s]", text, addr);
 }
 
-static void client_hash_check_idle(void *key, void *value __attr_unused__,
-				   void *context __attr_unused__)
+static void client_check_idle(struct pop3_client *client)
 {
-	struct pop3_client *client = key;
-
 	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT)
 		client_destroy(client, "Disconnected: Inactivity");
 }
 
 static void idle_timeout(void *context __attr_unused__)
 {
-	hash_foreach(clients, client_hash_check_idle, NULL);
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct pop3_client *client = key;
+
+		client_check_idle(client);
+	}
+	hash_iterate_deinit(iter);
 }
 
 unsigned int clients_get_count(void)
@@ -363,31 +364,35 @@
 	return hash_size(clients);
 }
 
-static void client_hash_check_io(void *key, void *value __attr_unused__,
-				 void *context __attr_unused__)
-{
-	struct pop3_client *client = key;
-
-	if (client->input_blocked) {
-		client->input_blocked = FALSE;
-		client_input(client);
-	}
-}
-
 void clients_notify_auth_connected(void)
 {
-	hash_foreach(clients, client_hash_check_io, NULL);
-}
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct pop3_client *client = key;
 
-static void client_hash_destroy(void *key, void *value __attr_unused__,
-				void *context __attr_unused__)
-{
-	client_destroy(key, NULL);
+		if (client->input_blocked) {
+			client->input_blocked = FALSE;
+			client_input(client);
+		}
+	}
+	hash_iterate_deinit(iter);
 }
 
 void clients_destroy_all(void)
 {
-	hash_foreach(clients, client_hash_destroy, NULL);
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_iterate_init(clients);
+	while (hash_iterate(iter, &key, &value)) {
+		struct pop3_client *client = key;
+
+		client_destroy(client, NULL);
+	}
+	hash_iterate_deinit(iter);
 }
 
 void clients_init(void)