/* Jungle Monkey * Copyright (C) 1999, 2000 The Regents of the University of Michigan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "sha_async.h" typedef struct _GSHAAsyncState { gboolean waiting; gchar* pathname; int fd; GIOChannel* iochannel; guint watch; GSHA* sha; GSHAAsyncFunc func; gpointer user_data; } GSHAAsyncState; #define MAX_ASYNC_SHA 8 static guint num_async_sha = 0; static GSList* waiting = NULL; static GSList* waiting_last = NULL; static gboolean dispatch_sha (GSHAAsyncState* state); static gboolean sha_cb (gpointer data); GSHAAsyncID* gnet_sha_new_file_async (const gchar* path, GSHAAsyncFunc func, gpointer user_data) { GSHAAsyncState* state; g_return_val_if_fail (path, NULL); g_return_val_if_fail (func, NULL); state = g_new0 (GSHAAsyncState, 1); state->pathname = g_strdup (path); state->sha = gnet_sha_new_incremental(); state->func = func; state->user_data = user_data; /* Dispatch now if no one is waiting, otherwise queue */ if (num_async_sha < MAX_ASYNC_SHA) { if (dispatch_sha (state)) state = NULL; } else { GSList* new_list; state->waiting = TRUE; /* Append waiting */ new_list = g_slist_alloc (); new_list->data = state; if (waiting_last) waiting_last->next = new_list; else waiting = new_list; waiting_last = new_list; } return (GSHAAsyncID) state; } /* Return FALSE if ok, TRUE otherwise */ static gboolean dispatch_sha (GSHAAsyncState* state) { g_return_val_if_fail (state, TRUE); g_return_val_if_fail (!state->fd, TRUE); g_return_val_if_fail (state->pathname, TRUE); state->waiting = FALSE; ++num_async_sha; /* Fail if file does not exist */ state->fd = open (state->pathname, O_RDONLY); /* TODO: A newer GLib may include an a way to open files non-blocking. */ if (state->fd == -1) { (state->func)(NULL, state->user_data); gnet_sha_new_file_async_cancel ((GSHAAsyncID) state); return TRUE; } state->iochannel = g_io_channel_unix_new (state->fd); state->watch = g_idle_add (sha_cb, state); return FALSE; } static gboolean sha_cb (gpointer data) { GSHAAsyncState* state = (GSHAAsyncState*) data; gchar buffer [4096]; guint bytes_read; g_return_val_if_fail (state, FALSE); bytes_read = read (state->fd, buffer, sizeof(buffer)); if (bytes_read > 0) { gnet_sha_update (state->sha, buffer, bytes_read); return TRUE; } else if (bytes_read == 0) { gnet_sha_final (state->sha); (state->func)(state->sha, state->user_data); state->sha = NULL; gnet_sha_new_file_async_cancel ((GSHAAsyncID) state); } else { (state->func)(NULL, state->user_data); gnet_sha_new_file_async_cancel ((GSHAAsyncID) state); } return FALSE; } /* Cancel or delete a SHA async state. This is used internally to delete states. */ void gnet_sha_new_file_async_cancel (GSHAAsyncID* id) { GSHAAsyncState* state = (GSHAAsyncState*) id; g_return_if_fail (id); g_free (state->pathname); gnet_sha_delete (state->sha); if (state->fd) close (state->fd); if (state->iochannel) g_io_channel_unref (state->iochannel); if (state->watch) g_source_remove (state->watch); if (state->waiting) { g_return_if_fail (waiting); g_return_if_fail (waiting_last); if (waiting_last->data == state) waiting_last = NULL; waiting = g_slist_remove (waiting, state); if (waiting && !waiting_last) waiting_last = g_slist_last (waiting); } else { --num_async_sha; if (waiting && num_async_sha < MAX_ASYNC_SHA) { GSHAAsyncState* next_state; /* Remove from front of list */ next_state = (GSHAAsyncState*) waiting->data; if (waiting == waiting_last) waiting_last = NULL; waiting = g_slist_remove (waiting, next_state); dispatch_sha (next_state); } } g_free (state); }