/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Unix fd watcher thread. * * By Peter Wang. * * See readme.txt for copyright information. * * This module implements a background thread that waits for data * to arrive in file descriptors, at which point it dispatches to * functions which will process that data. */ #include #include #include #include #include "allegro.h" #include "allegro/internal/aintern.h" #include "allegro/internal/aintern_thread.h" #include "allegro/internal/aintern_vector.h" typedef struct WATCH_ITEM { int fd; void (*callback)(void *); void *cb_data; } WATCH_ITEM; static _AL_THREAD fd_watch_thread; static _AL_MUTEX fd_watch_mutex = _AL_MUTEX_UNINITED; static _AL_VECTOR fd_watch_list = _AL_VECTOR_INITIALIZER(WATCH_ITEM); /* fd_watch_thread_func: [fdwatch thread] * The thread loop function. */ static void fd_watch_thread_func(_AL_THREAD *self, void *unused) { while (!_al_thread_should_stop(self)) { fd_set rfds; int max_fd; /* set up max_fd and rfds */ _al_mutex_lock(&fd_watch_mutex); { WATCH_ITEM *wi; unsigned int i; FD_ZERO(&rfds); max_fd = -1; for (i = 0; i < _al_vector_size(&fd_watch_list); i++) { wi = _al_vector_ref(&fd_watch_list, i); FD_SET(wi->fd, &rfds); if (wi->fd > max_fd) max_fd = wi->fd; } } _al_mutex_unlock(&fd_watch_mutex); /* wait for something to happen on one of the fds */ { struct timeval tv; int retval; tv.tv_sec = 0; tv.tv_usec = 250000; retval = select(max_fd+1, &rfds, NULL, NULL, &tv); if (retval < 1) continue; } /* one or more of the fds has activity */ _al_mutex_lock(&fd_watch_mutex); { WATCH_ITEM *wi; unsigned int i; for (i = 0; i < _al_vector_size(&fd_watch_list); i++) { wi = _al_vector_ref(&fd_watch_list, i); if (FD_ISSET(wi->fd, &rfds)) wi->callback(wi->cb_data); } } _al_mutex_unlock(&fd_watch_mutex); } } /* _al_unix_start_watching_fd: [primary thread] * * Start watching for data on file descriptor `fd'. This is done in * a background thread, which is started if necessary. When there is * data waiting to be read on fd, `callback' is applied to `cb_data'. * The callback function should read as much data off fd as possible. * * Note: the callback is run from the background thread. You can * assume there is only one callback being called from the fdwatch * module at a time. */ void _al_unix_start_watching_fd(int fd, void (*callback)(void *), void *cb_data) { ASSERT(fd >= 0); ASSERT(callback); /* start the background thread if necessary */ if (_al_vector_size(&fd_watch_list) == 0) { _al_mutex_init(&fd_watch_mutex); _al_thread_create(&fd_watch_thread, fd_watch_thread_func, NULL); } /* now add the watch item to the list */ _al_mutex_lock(&fd_watch_mutex); { WATCH_ITEM *wi = _al_vector_alloc_back(&fd_watch_list); wi->fd = fd; wi->callback = callback; wi->cb_data = cb_data; } _al_mutex_unlock(&fd_watch_mutex); } /* _al_unix_stop_watching_fd: [primary thread] * * Stop watching for data on `fd'. Once there are no more file * descriptors to watch, the background thread will be stopped. This * function is synchronised with the background thread, so you don't * have to do any locking before calling it. */ void _al_unix_stop_watching_fd(int fd) { bool list_empty = false; /* find the fd in the watch list and remove it */ _al_mutex_lock(&fd_watch_mutex); { WATCH_ITEM *wi; unsigned int i; for (i = 0; i < _al_vector_size(&fd_watch_list); i++) { wi = _al_vector_ref(&fd_watch_list, i); if (wi->fd == fd) { _al_vector_delete_at(&fd_watch_list, i); list_empty = _al_vector_is_empty(&fd_watch_list); break; } } } _al_mutex_unlock(&fd_watch_mutex); /* if no more fd's are being watched, stop the background thread */ if (list_empty) { _al_thread_join(&fd_watch_thread); _al_mutex_destroy(&fd_watch_mutex); _al_vector_free(&fd_watch_list); } } /* * Local Variables: * c-basic-offset: 3 * indent-tabs-mode: nil * End: */