/* * Waitable timers management * * Copyright (C) 1999 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "wine/port.h" #include #include #include #include #include #include #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winternl.h" #include "file.h" #include "handle.h" #include "request.h" struct timer { struct object obj; /* object header */ int manual; /* manual reset */ int signaled; /* current signaled state */ int period; /* timer period in ms */ struct timeval when; /* next expiration */ struct timeout_user *timeout; /* timeout user */ struct thread *thread; /* thread that set the APC function */ void *callback; /* callback APC function */ void *arg; /* callback argument */ }; static void timer_dump( struct object *obj, int verbose ); static int timer_signaled( struct object *obj, struct thread *thread ); static int timer_satisfied( struct object *obj, struct thread *thread ); static void timer_destroy( struct object *obj ); static const struct object_ops timer_ops = { sizeof(struct timer), /* size */ timer_dump, /* dump */ add_queue, /* add_queue */ remove_queue, /* remove_queue */ timer_signaled, /* signaled */ timer_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ no_lookup_name, /* lookup_name */ no_close_handle, /* close_handle */ timer_destroy /* destroy */ }; /* create a timer object */ static struct timer *create_timer( struct directory *root, const struct unicode_str *name, unsigned int attr, int manual ) { struct timer *timer; if ((timer = create_named_object_dir( root, name, attr, &timer_ops ))) { if (get_error() != STATUS_OBJECT_NAME_EXISTS) { /* initialize it if it didn't already exist */ timer->manual = manual; timer->signaled = 0; timer->when.tv_sec = 0; timer->when.tv_usec = 0; timer->period = 0; timer->timeout = NULL; timer->thread = NULL; } } return timer; } /* callback on timer expiration */ static void timer_callback( void *private ) { struct timer *timer = (struct timer *)private; /* queue an APC */ if (timer->thread) { if (!thread_queue_apc( timer->thread, &timer->obj, timer->callback, APC_TIMER, 0, (void *)timer->when.tv_sec, (void *)timer->when.tv_usec, timer->arg)) { release_object( timer->thread ); timer->thread = NULL; } } if (timer->period) /* schedule the next expiration */ { add_timeout( &timer->when, timer->period ); timer->timeout = add_timeout_user( &timer->when, timer_callback, timer ); } else timer->timeout = NULL; /* wake up waiters */ timer->signaled = 1; wake_up( &timer->obj, 0 ); } /* cancel a running timer */ static int cancel_timer( struct timer *timer ) { int signaled = timer->signaled; if (timer->timeout) { remove_timeout_user( timer->timeout ); timer->timeout = NULL; } if (timer->thread) { thread_cancel_apc( timer->thread, &timer->obj, 0 ); release_object( timer->thread ); timer->thread = NULL; } return signaled; } /* set the timer expiration and period */ static int set_timer( struct timer *timer, const abs_time_t *expire, int period, void *callback, void *arg ) { int signaled = cancel_timer( timer ); if (timer->manual) { period = 0; /* period doesn't make any sense for a manual timer */ timer->signaled = 0; } if (!expire->sec && !expire->usec) { /* special case: use now + period as first expiration */ gettimeofday( &timer->when, NULL ); add_timeout( &timer->when, period ); } else { timer->when.tv_sec = expire->sec; timer->when.tv_usec = expire->usec; } timer->period = period; timer->callback = callback; timer->arg = arg; if (callback) timer->thread = (struct thread *)grab_object( current ); timer->timeout = add_timeout_user( &timer->when, timer_callback, timer ); return signaled; } static void timer_dump( struct object *obj, int verbose ) { struct timer *timer = (struct timer *)obj; assert( obj->ops == &timer_ops ); fprintf( stderr, "Timer manual=%d when=%ld.%06ld period=%d ", timer->manual, timer->when.tv_sec, timer->when.tv_usec, timer->period ); dump_object_name( &timer->obj ); fputc( '\n', stderr ); } static int timer_signaled( struct object *obj, struct thread *thread ) { struct timer *timer = (struct timer *)obj; assert( obj->ops == &timer_ops ); return timer->signaled; } static int timer_satisfied( struct object *obj, struct thread *thread ) { struct timer *timer = (struct timer *)obj; assert( obj->ops == &timer_ops ); if (!timer->manual) timer->signaled = 0; return 0; } static void timer_destroy( struct object *obj ) { struct timer *timer = (struct timer *)obj; assert( obj->ops == &timer_ops ); if (timer->timeout) remove_timeout_user( timer->timeout ); if (timer->thread) release_object( timer->thread ); } /* create a timer */ DECL_HANDLER(create_timer) { struct timer *timer; struct unicode_str name; struct directory *root = NULL; reply->handle = 0; get_req_unicode_str( &name ); if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 ))) return; if ((timer = create_timer( root, &name, req->attributes, req->manual ))) { reply->handle = alloc_handle( current->process, timer, req->access, req->attributes ); release_object( timer ); } if (root) release_object( root ); } /* open a handle to a timer */ DECL_HANDLER(open_timer) { struct unicode_str name; struct directory *root = NULL; struct timer *timer; get_req_unicode_str( &name ); if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 ))) return; if ((timer = open_object_dir( root, &name, req->attributes, &timer_ops ))) { reply->handle = alloc_handle( current->process, &timer->obj, req->access, req->attributes ); release_object( timer ); } if (root) release_object( root ); } /* set a waitable timer */ DECL_HANDLER(set_timer) { struct timer *timer; if ((timer = (struct timer *)get_handle_obj( current->process, req->handle, TIMER_MODIFY_STATE, &timer_ops ))) { reply->signaled = set_timer( timer, &req->expire, req->period, req->callback, req->arg ); release_object( timer ); } } /* cancel a waitable timer */ DECL_HANDLER(cancel_timer) { struct timer *timer; if ((timer = (struct timer *)get_handle_obj( current->process, req->handle, TIMER_MODIFY_STATE, &timer_ops ))) { reply->signaled = cancel_timer( timer ); release_object( timer ); } } /* Get information on a waitable timer */ DECL_HANDLER(get_timer_info) { struct timer *timer; if ((timer = (struct timer *)get_handle_obj( current->process, req->handle, TIMER_QUERY_STATE, &timer_ops ))) { reply->when.sec = timer->when.tv_sec; reply->when.usec = timer->when.tv_usec; reply->signaled = timer->signaled; release_object( timer ); } }