Added exception handling wrapper to a number of server requests.
[wine] / server / timer.c
1 /*
2  * Waitable timers management
3  *
4  * Copyright (C) 1999 Alexandre Julliard
5  */
6
7 #include <assert.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <sys/time.h>
11 #include <sys/types.h>
12
13 #include "winnt.h"
14 #include "handle.h"
15 #include "request.h"
16
17 struct timer
18 {
19     struct object        obj;       /* object header */
20     int                  manual;    /* manual reset */
21     int                  signaled;  /* current signaled state */
22     int                  period;    /* timer period in ms */
23     struct timeval       when;      /* next expiration */
24     struct timeout_user *timeout;   /* timeout user */
25     struct thread       *thread;    /* thread that set the APC function */
26     void                *callback;  /* callback APC function */
27     void                *arg;       /* callback argument */
28 };
29
30 static void timer_dump( struct object *obj, int verbose );
31 static int timer_signaled( struct object *obj, struct thread *thread );
32 static int timer_satisfied( struct object *obj, struct thread *thread );
33 static void timer_destroy( struct object *obj );
34
35 static const struct object_ops timer_ops =
36 {
37     sizeof(struct timer),      /* size */
38     timer_dump,                /* dump */
39     add_queue,                 /* add_queue */
40     remove_queue,              /* remove_queue */
41     timer_signaled,            /* signaled */
42     timer_satisfied,           /* satisfied */
43     NULL,                      /* get_poll_events */
44     NULL,                      /* poll_event */
45     no_read_fd,                /* get_read_fd */
46     no_write_fd,               /* get_write_fd */
47     no_flush,                  /* flush */
48     no_get_file_info,          /* get_file_info */
49     timer_destroy              /* destroy */
50 };
51
52
53 /* create a timer object */
54 static struct timer *create_timer( const WCHAR *name, size_t len, int manual )
55 {
56     struct timer *timer;
57
58     if ((timer = create_named_object( &timer_ops, name, len )))
59     {
60         if (get_error() != STATUS_OBJECT_NAME_COLLISION)
61         {
62             /* initialize it if it didn't already exist */
63             timer->manual       = manual;
64             timer->signaled     = 0;
65             timer->when.tv_sec  = 0;
66             timer->when.tv_usec = 0;
67             timer->period       = 0;
68             timer->timeout      = NULL;
69             timer->thread       = NULL;
70         }
71     }
72     return timer;
73 }
74
75 /* callback on timer expiration */
76 static void timer_callback( void *private )
77 {
78     struct timer *timer = (struct timer *)private;
79
80     /* queue an APC */
81     if (timer->thread)
82         thread_queue_apc( timer->thread, &timer->obj, timer->callback, APC_TIMER, 3,
83                           (void *)timer->when.tv_sec, (void *)timer->when.tv_usec, timer->arg );
84
85     if (timer->period)  /* schedule the next expiration */
86     {
87         add_timeout( &timer->when, timer->period );
88         timer->timeout = add_timeout_user( &timer->when, timer_callback, timer );
89     }
90     else timer->timeout = NULL;
91
92     /* wake up waiters */
93     timer->signaled = 1;
94     wake_up( &timer->obj, 0 );
95 }
96
97 /* cancel a running timer */
98 static void cancel_timer( struct timer *timer )
99 {
100     if (timer->timeout)
101     {
102         remove_timeout_user( timer->timeout );
103         timer->timeout = NULL;
104     }
105     if (timer->thread)
106     {
107         thread_cancel_apc( timer->thread, &timer->obj );
108         timer->thread = NULL;
109     }
110 }
111
112 /* set the timer expiration and period */
113 static void set_timer( struct timer *timer, int sec, int usec, int period,
114                        void *callback, void *arg )
115 {
116     cancel_timer( timer );
117     if (timer->manual)
118     {
119         period = 0;  /* period doesn't make any sense for a manual timer */
120         timer->signaled = 0;
121     }
122     if (!sec && !usec)
123     {
124         /* special case: use now + period as first expiration */
125         gettimeofday( &timer->when, 0 );
126         add_timeout( &timer->when, period );
127     }
128     else
129     {
130         timer->when.tv_sec  = sec;
131         timer->when.tv_usec = usec;
132     }
133     timer->period       = period;
134     timer->callback     = callback;
135     timer->arg          = arg;
136     if (callback) timer->thread = current;
137     timer->timeout = add_timeout_user( &timer->when, timer_callback, timer );
138 }
139
140 static void timer_dump( struct object *obj, int verbose )
141 {
142     struct timer *timer = (struct timer *)obj;
143     assert( obj->ops == &timer_ops );
144     fprintf( stderr, "Timer manual=%d when=%ld.%06ld period=%d ",
145              timer->manual, timer->when.tv_sec, timer->when.tv_usec, timer->period );
146     dump_object_name( &timer->obj );
147     fputc( '\n', stderr );
148 }
149
150 static int timer_signaled( struct object *obj, struct thread *thread )
151 {
152     struct timer *timer = (struct timer *)obj;
153     assert( obj->ops == &timer_ops );
154     return timer->signaled;
155 }
156
157 static int timer_satisfied( struct object *obj, struct thread *thread )
158 {
159     struct timer *timer = (struct timer *)obj;
160     assert( obj->ops == &timer_ops );
161     if (!timer->manual) timer->signaled = 0;
162     return 0;
163 }
164
165 static void timer_destroy( struct object *obj )
166 {
167     struct timer *timer = (struct timer *)obj;
168     assert( obj->ops == &timer_ops );
169
170     if (timer->timeout) remove_timeout_user( timer->timeout );
171 }
172
173 /* create a timer */
174 DECL_HANDLER(create_timer)
175 {
176     struct timer *timer;
177
178     req->handle = -1;
179     if ((timer = create_timer( get_req_data(req), get_req_data_size(req), req->manual )))
180     {
181         req->handle = alloc_handle( current->process, timer, TIMER_ALL_ACCESS, req->inherit );
182         release_object( timer );
183     }
184 }
185
186 /* open a handle to a timer */
187 DECL_HANDLER(open_timer)
188 {
189     req->handle = open_object( get_req_data(req), get_req_data_size(req),
190                                &timer_ops, req->access, req->inherit );
191 }
192
193 /* set a waitable timer */
194 DECL_HANDLER(set_timer)
195 {
196     struct timer *timer;
197
198     if ((timer = (struct timer *)get_handle_obj( current->process, req->handle,
199                                                  TIMER_MODIFY_STATE, &timer_ops )))
200     {
201         set_timer( timer, req->sec, req->usec, req->period, req->callback, req->arg );
202         release_object( timer );
203     }
204 }
205
206 /* cancel a waitable timer */
207 DECL_HANDLER(cancel_timer)
208 {
209     struct timer *timer;
210
211     if ((timer = (struct timer *)get_handle_obj( current->process, req->handle,
212                                                  TIMER_MODIFY_STATE, &timer_ops )))
213     {
214         cancel_timer( timer );
215         release_object( timer );
216     }
217 }