[DAPL2] DAPL Counters & 2.0.3 extensions to support counter retrieval.
[mirror/winof/.git] / ulp / dapl2 / dapl / common / dapl_timer_util.c
1 /*\r
2  * Copyright (c) 2002-2003, Network Appliance, Inc. All rights reserved.\r
3  *\r
4  * This Software is licensed under one of the following licenses:\r
5  *\r
6  * 1) under the terms of the "Common Public License 1.0" a copy of which is\r
7  *    available from the Open Source Initiative, see\r
8  *    http://www.opensource.org/licenses/cpl.php.\r
9  *\r
10  * 2) under the terms of the "The BSD License" a copy of which is\r
11  *    available from the Open Source Initiative, see\r
12  *    http://www.opensource.org/licenses/bsd-license.php.\r
13  *\r
14  * 3) under the terms of the "GNU General Public License (GPL) Version 2" a\r
15  *    copy of which is available from the Open Source Initiative, see\r
16  *    http://www.opensource.org/licenses/gpl-license.php.\r
17  *\r
18  * Licensee has the right to choose one of the above licenses.\r
19  *\r
20  * Redistributions of source code must retain the above copyright\r
21  * notice and one of the license notices.\r
22  *\r
23  * Redistributions in binary form must reproduce both the above copyright\r
24  * notice, one of the license notices in the documentation\r
25  * and/or other materials provided with the distribution.\r
26  */\r
27 \r
28 /**********************************************************************\r
29  *\r
30  * MODULE: dapl_timer_util.c\r
31  *\r
32  * PURPOSE: DAPL timer management\r
33  * Description: Routines to add and cancel timer records. A timer record\r
34  *              is put on the global timer queue. If the timer thread is\r
35  *              not running, start it. The timer thread will sleep\r
36  *              until a timer event or until a process wakes it up\r
37  *              to notice a new timer is available; we use a DAPL_WAIT_OBJ\r
38  *              for synchronization.\r
39  *\r
40  *              If a timer is cancelled, it is simlpy removed from the\r
41  *              queue. The timer may wakeup and notice there is no timer\r
42  *              record to awaken at this time, so it will reset for the\r
43  *              next entry. When there are no timer records to manage,\r
44  *              the timer thread just sleeps until awakened.\r
45  *\r
46  *              This file also contains the timer handler thread,\r
47  *              embodied in dapls_timer_thread().\r
48  *\r
49  * $Id:$\r
50  **********************************************************************/\r
51 \r
52 #include "dapl.h"\r
53 #include "dapl_timer_util.h"\r
54 \r
55 \r
56 struct timer_head\r
57 {\r
58     DAPL_LLIST_HEAD             timer_list_head;\r
59     DAPL_OS_LOCK                lock;\r
60     DAPL_OS_WAIT_OBJECT         wait_object;\r
61     DAPL_OS_THREAD              timeout_thread_handle;\r
62 } g_daplTimerHead;\r
63 \r
64 typedef struct timer_head DAPL_TIMER_HEAD;\r
65 \r
66 \r
67 void dapls_timer_thread (void   *arg );\r
68 \r
69 void\r
70 dapls_timer_init ()\r
71 {\r
72     /*\r
73      * Set up the timer thread elements. The timer thread isn't\r
74      * started until it is actually needed\r
75      */\r
76     g_daplTimerHead.timer_list_head = NULL;\r
77     dapl_os_lock_init ( &g_daplTimerHead.lock );\r
78     dapl_os_wait_object_init ( &g_daplTimerHead.wait_object );\r
79     g_daplTimerHead.timeout_thread_handle = 0;\r
80 }\r
81 \r
82 \r
83 /*\r
84  * dapls_timer_set\r
85  *\r
86  * Set a timer. The timer will invoke the specified function\r
87  * after a number of useconds expires.\r
88  *\r
89  * Input:\r
90  *      timer    User provided timer structure\r
91  *      func     Function to invoke when timer expires\r
92  *      data     Argument passed to func()\r
93  *      expires  microseconds until timer fires\r
94  *\r
95  * Returns:\r
96  *      no return value\r
97  *\r
98  */\r
99 DAT_RETURN\r
100 dapls_timer_set (\r
101         IN  DAPL_OS_TIMER               *timer,\r
102         IN  void                        (*func) (uintptr_t),\r
103         IN  void                        *data,\r
104         IN  DAPL_OS_TIMEVAL             expires )\r
105 {\r
106     DAPL_OS_TIMER               *list_ptr;\r
107     DAPL_OS_TIMEVAL             cur_time;\r
108     DAT_BOOLEAN                 wakeup_tmo_thread;\r
109 \r
110     /*\r
111      * Start the timer thread the first time we need a timer\r
112      */\r
113     if ( g_daplTimerHead.timeout_thread_handle == 0 )\r
114     {\r
115         dapl_os_thread_create ( dapls_timer_thread,\r
116                                 &g_daplTimerHead,\r
117                                 &g_daplTimerHead.timeout_thread_handle );\r
118     }\r
119 \r
120     dapl_llist_init_entry ( &timer->list_entry);\r
121     wakeup_tmo_thread = DAT_FALSE;\r
122     dapl_os_get_time ( &cur_time );\r
123     timer->expires  = cur_time + expires; /* calculate future time */\r
124     timer->function = func;\r
125     timer->data     = data;\r
126 \r
127     /*\r
128      * Put the element on the queue: sorted by wakeup time, eariliest\r
129      * first.\r
130      */\r
131     dapl_os_lock ( &g_daplTimerHead.lock );\r
132     /*\r
133      * Deal with 3 cases due to our list structure:\r
134      * 1) list is empty: become the list head\r
135      * 2) New timer is sooner than list head: become the list head\r
136      * 3) otherwise, sort the timer into the list, no need to wake\r
137      *    the timer thread up\r
138      */\r
139     if ( dapl_llist_is_empty ( &g_daplTimerHead.timer_list_head) )\r
140     {\r
141         /* Case 1: add entry to head of list */\r
142         dapl_llist_add_head ( &g_daplTimerHead.timer_list_head,\r
143                               (DAPL_LLIST_ENTRY *)&timer->list_entry,\r
144                               timer );\r
145         wakeup_tmo_thread = DAT_TRUE;\r
146     }\r
147     else\r
148     {\r
149         list_ptr = (DAPL_OS_TIMER *)\r
150                 dapl_llist_peek_head (&g_daplTimerHead.timer_list_head);\r
151 \r
152         if ( timer->expires < list_ptr->expires )\r
153         {\r
154             /* Case 2: add entry to head of list */\r
155             dapl_llist_add_head ( &g_daplTimerHead.timer_list_head,\r
156                                   (DAPL_LLIST_ENTRY *)&timer->list_entry,\r
157                                   timer );\r
158             wakeup_tmo_thread = DAT_TRUE;\r
159         }\r
160         else\r
161         {\r
162             /* Case 3: figure out where entry goes in sorted list */\r
163             list_ptr = dapl_llist_next_entry (\r
164                             &g_daplTimerHead.timer_list_head,\r
165                             (DAPL_LLIST_ENTRY *)&list_ptr->list_entry);\r
166 \r
167             while (list_ptr != NULL)\r
168             {\r
169                 if ( timer->expires < list_ptr->expires )\r
170                 {\r
171                     dapl_llist_add_entry ( &g_daplTimerHead.timer_list_head,\r
172                                            (DAPL_LLIST_ENTRY *)&list_ptr->list_entry,\r
173                                            (DAPL_LLIST_ENTRY *)&timer->list_entry,\r
174                                            timer );\r
175                     break;\r
176 \r
177                 }\r
178                 list_ptr = dapl_llist_next_entry (\r
179                                 &g_daplTimerHead.timer_list_head,\r
180                                 (DAPL_LLIST_ENTRY *)&list_ptr->list_entry);\r
181             }\r
182             if (list_ptr == NULL)\r
183             {\r
184                 /* entry goes to the end of the list */\r
185                 dapl_llist_add_tail ( &g_daplTimerHead.timer_list_head,\r
186                                       (DAPL_LLIST_ENTRY *)&timer->list_entry,\r
187                                       timer );\r
188             }\r
189         }\r
190 \r
191     }\r
192     dapl_os_unlock ( &g_daplTimerHead.lock );\r
193 \r
194     if (wakeup_tmo_thread == DAT_TRUE)\r
195     {\r
196         dapl_os_wait_object_wakeup (&g_daplTimerHead.wait_object);\r
197     }\r
198 \r
199     return DAT_SUCCESS;\r
200 }\r
201 \r
202 \r
203 /*\r
204  * dapls_os_timer_cancel\r
205  *\r
206  * Cancel a timer. Simply deletes the timer with no function invocations\r
207  *\r
208  * Input:\r
209  *      timer    User provided timer structure\r
210  *\r
211  * Returns:\r
212  *      no return value\r
213  */\r
214 void\r
215 dapls_timer_cancel (\r
216         IN  DAPL_OS_TIMER               *timer)\r
217 {\r
218     dapl_os_lock ( &g_daplTimerHead.lock );\r
219     /*\r
220      * make sure the entry has not been removed by another thread\r
221      */\r
222     if ( ! dapl_llist_is_empty ( &g_daplTimerHead.timer_list_head ) &&\r
223          timer->list_entry.list_head == &g_daplTimerHead.timer_list_head )\r
224     {\r
225         dapl_llist_remove_entry ( &g_daplTimerHead.timer_list_head,\r
226                                   (DAPL_LLIST_ENTRY *)&timer->list_entry );\r
227     }\r
228     /*\r
229      * If this was the first entry on the queue we could awaken the\r
230      * thread and have it reset the list; but it will just wake up\r
231      * and find that the timer entry has been removed, then go back\r
232      * to sleep, so don't bother.\r
233      */\r
234     dapl_os_unlock ( &g_daplTimerHead.lock );\r
235 }\r
236 \r
237 \r
238 /*\r
239  * dapls_timer_thread\r
240  *\r
241  * Core worker thread dealing with all timers. Basic algorithm:\r
242  *      - Sleep until work shows up\r
243  *      - Take first element of sorted timer list and wake\r
244  *        invoke the callback if expired\r
245  *      - Sleep for the timeout period if not expired\r
246  *\r
247  * Input:\r
248  *      timer_head    Timer head structure to manage timer lists\r
249  *\r
250  * Returns:\r
251  *      no return value\r
252  */\r
253 void\r
254 dapls_timer_thread (\r
255         void                    *arg )\r
256 {\r
257     DAPL_OS_TIMER               *list_ptr;\r
258     DAPL_OS_TIMEVAL             cur_time;\r
259     DAT_RETURN                  dat_status;\r
260     DAPL_TIMER_HEAD             *timer_head;\r
261 \r
262     timer_head = arg;\r
263 \r
264     for (;;)\r
265     {\r
266         if ( dapl_llist_is_empty ( &timer_head->timer_list_head ) )\r
267         {\r
268             dat_status = dapl_os_wait_object_wait (&timer_head->wait_object,\r
269                                                    DAT_TIMEOUT_INFINITE );\r
270         }\r
271 \r
272         /*\r
273          * Lock policy:\r
274          * While this thread is accessing the timer list, it holds the\r
275          * lock.  Otherwise, it doesn't.\r
276          */\r
277         dapl_os_lock ( &timer_head->lock );\r
278         while ( ! dapl_llist_is_empty ( &timer_head->timer_list_head) )\r
279         {\r
280             list_ptr = (DAPL_OS_TIMER *)\r
281                 dapl_llist_peek_head ( &g_daplTimerHead.timer_list_head );\r
282             dapl_os_get_time ( &cur_time );\r
283 \r
284             if ( list_ptr->expires <= cur_time )\r
285             {\r
286                 /*\r
287                  * Remove the entry from the list. Sort out how much\r
288                  * time we need to sleep for the next one\r
289                  */\r
290                 list_ptr = dapl_llist_remove_head ( &timer_head->timer_list_head );\r
291                 dapl_os_unlock ( &timer_head->lock );\r
292 \r
293                 /*\r
294                  * Invoke the user callback\r
295                  */\r
296                 list_ptr->function ( (uintptr_t)list_ptr->data );\r
297                 /* timer structure was allocated by caller, we don't\r
298                  * free it here.\r
299                  */\r
300 \r
301                 /* reacquire the lock */\r
302                 dapl_os_lock ( &timer_head->lock );\r
303             }\r
304             else\r
305             {\r
306                 dapl_os_unlock ( &timer_head->lock );\r
307                 dat_status =\r
308                     dapl_os_wait_object_wait ( &timer_head->wait_object,\r
309                                                (DAT_TIMEOUT) (list_ptr->expires - cur_time) );\r
310                 dapl_os_lock ( &timer_head->lock );\r
311             }\r
312         }\r
313         /*\r
314          * release the lock before going back to the top to sleep\r
315          */\r
316         dapl_os_unlock ( &timer_head->lock );\r
317 \r
318         if ( dat_status == DAT_INTERNAL_ERROR )\r
319         {\r
320             /*\r
321              * XXX What do we do here?\r
322              */\r
323         }\r
324     } /* for (;;) */\r
325 }\r
326 \r
327 \r
328 \r
329 /*\r
330  * Local variables:\r
331  *  c-indent-level: 4\r
332  *  c-basic-offset: 4\r
333  *  tab-width: 8\r
334  * End:\r
335  */\r